OpenGL投影矩阵

概述

电脑显示器是2D平面,一个3D物体通过OpenGL渲染投影到2D显示器平面形成图像,GL_PROJECTION是OpenGL用于投影转换的矩阵。首先,转换3D物体的顶点数据从相机空间到裁剪空间,然后,通过除以W(齐次坐标)从裁剪空间再转换到标准设备空间(NDC)。
因此,我们需要意识到裁剪计算和NDC转换都是整合到了GL_PROJECTION矩阵里了。接下来的内容将探讨怎么构建投影矩阵,使用6个代表边界值的参数分别是:left,right,bottom,top,near , far(下面简写为l、r、b、t、n、f)。有一点注意一下,裁剪计算是在转换成NDC坐标之前发生的,即在除以(齐次坐标)之前。通过裁剪坐标,,和(这里的c下标表示裁剪空间)做对比,如果裁剪坐标小于或大于将被裁减掉(),然后OpenGL会重新构建网格边缘。

一个被平头视锥裁剪的三角形

透视投影

在透视投影里,一个3D点在一个平头视锥体里面(相机空间)是要被映射到一个正方体(NDC空间)里的,x坐标的区间从[l,r]映射到[-1,1],y坐标的区间从[b,t]映射到[-1,1],z坐标的区间从[-n,-f]映射到[-1,1]。

投影视锥和标准设备空间(NDC)

注意一点,相机空间是使用右手坐标系统,但是NDC坐标空间使用左手坐标系统,这就是相机在原点沿着-Z方向看,但是在NDC空间是反过来的。因为glFrustum()仅仅接收正的near和far值,在构造GL_PROJECTION矩阵时我们需要给他们变成负值。
在OpenGL里,一个3D点在相机空间是被投影到near平面(projection plane 即下图的-n平面)。下面的图片展示一个点( )在相机空间投影到near平面上的点( )。
从上往下看视锥体

从侧面看视锥体

从上往下看视锥体,相机空间的x坐标点 投影到 ,通过相似三角形原理得: = => = =
同理,从侧边看视锥体可得: = => = =
注意到, 和 都依赖 ,他们和 成相反的比例,也就是说他们都需要除以 。这是非常有用的线索,用于构建GL_PROJECTION矩阵上,通过乘以GL_PROJECTION矩阵从相机空间转换到裁剪空间,坐标仍然是齐次坐标,除以裁剪空间的w最终变成规范化设备坐标空间(NDC)(更多资料请查阅OpenGL Transformation)
投影矩阵乘以相机空间坐标得到裁剪空间坐标
裁剪空间坐标除以W得到NDC空间坐标

因此,我们可以设置裁剪空间的w坐标等于 ,然后投影矩阵GL_PROJECTION的第四行就变成(0,0,-1,0)。
根据矩阵乘法公式计算得,裁剪空间的w值等于相机空间的-z值

下一步,我们通过线性关系映射(p下标表示投影空间未有裁剪的)和(p下标表示投影空间未有裁剪的)到NDC坐标空间的(n下标表示NDC空间)和(n下标表示NDC空间),即:[l,r] => [-1,1] [b,t] => [-1,1]

为了直观点,可以把映射值连成的直线平移经过原点再平移回来
映射的最大点和最小点是已知的,代入上式可以求出平移值推导出映射公式

为了直观点,可以把映射值连成的直线平移经过原点再平移回来
映射的最大点和最小点是已知的,代入上式可以求出平移值推导出映射公式

然后使用上面的 和 两个等式替换到 和 等式里:
求出NDC空间坐标,构造成除以w的形式
求出NDC空间坐标,构造成除以w的形式

注意到,我们故意把等式构造成除以 的形式,即与透视除法( )等同,只要把 看成 上面等式括号的值就是裁剪空间的 和 。
从上面的等式总结,我们可以找到GL_PROJECTION矩阵第一行和第二行的构造方法
裁剪空间坐标等于投影矩阵乘以相机空间坐标

现在GL_PROJECTION矩阵只剩下第三行没构造好,构造 稍微不同于 、 ,因为在相机空间ze总是投影在near平面的-n点上,由于我们需要z值方便做深度测试和裁剪,另外也需要逆转换投影,我们知道z是不依赖于x或y的值,我们可以借用w表示 和 之间的关系,因此我们可以构造GL_PROJECTION矩阵的第三行如下图:

z是不依赖于x和y的,因此第三行矩阵x和y的值是0

在相机空间, 等于1,因此等式变成:
在相机空间的坐标w为1,因此可以略去

为了求出系数A和B,根据上面等式得出 和 的关系,假设 等于-n(即近平面)那么 等于-1(即映射到NDC空间的坐标应为-1), 等于-f(即远平面)那么 等于1(映射到NDC空间的坐标应为1),带入上面等式:

由等式(1)推出:

把B等式代入等式(2):

把A等式代入等式(1):


得到A和B的等式可导出 和 的关系等式:
NDC空间坐标的z值和相机空间坐标的z值的关系等式

最终,可得到整个GL_PROJECTION矩阵:
完整的投影矩阵公式

当视锥体是个对称视锥体时,r = -l , t = -b 所以可以简化矩阵公式:


最后,再审视一下上面等式(3)表达的 和 的关系。注意到这个等式函数是非线性关系的,意思是在近平面时会有非常高的精度,反过来在远平面会有非常低的精度。如果区间[-n,-f]是个很大的范围会导致出现深度(z值)精度的问题(z-fighting), 改变很小的值不会计算得到对应的 的值。所以,n和f之间的区间应该小点以减少z深度的精度问题。
z-fighting,可以看到右图跨度大,打圈处的f值由于精度问题都会对应1

正交投影

构造正交投影矩阵 GL_PROJECT比构造透视投影矩阵简单多了,只需要在相机空间里线性的映射到NDC空间。我们只需要缩放长方体变成正方体,然后移到原点位置,让我们看看GL_PROJECTION的各元素之间的线性关系吧。


相机空间的x映射到NDC空间的x,同透视投用一样,可以先平移到原点再平移回来

注意,没有投影,NDC空间的x直接和相机空间的x成线性关系
相机空间的y映射到NDC空间的y,同透视投用一样,可以先平移到原点再平移回来
注意,没有投影,NDC空间的y直接和相机空间的y成线性关系
相机空间的z映射到NDC空间的z,可以先平移到原点再平移回来

注意,没有投影,NDC空间的z直接和相机空间的z成线性关系

因为正交投影矩阵是不需要用w计算z值的(z值也是线性关系直接求出来,透视投影需要用w来间接计算 的关系),矩阵GL_PROJECTION的第四行保留为(0,0,0,1),因此完整的正交投影矩阵为:
正交投影矩阵公式

当视锥体是对称长方体,即r = -l、t = -b,有:

坐标转换:模型坐标 -> 世界坐标 -> 相机坐标 -> 裁剪坐标 -> NDC坐标
原文链接

你可能感兴趣的:(OpenGL投影矩阵)