【翻译】OpenGL 投影矩阵
概述
电脑显示器是一个 2D 平面。一个通过 OpenGL 渲染的场景必须作为一个 2D 图像投影到电脑屏幕上。矩阵 GL_PROJECTION 就是用来做这个投影转换的。首先,它将顶点数据从视点坐标转换到裁剪坐标。然后,这些裁剪坐标通过除以 w 分量又被转换到标准化设备坐标(NDC)。
![](https://img.haomeiwen.com/i263545/fc0b027f6e7bae14.png)
因此,我们必须记住,裁剪(截头椎体剔除)和 NDC 转换都被整合到了 GL_PROJECTION 矩阵中。接下来的章节讲述如何用 6 个参数来构建投影矩阵:left,right,bottom,top,near 和 far 边界值。
注意,截头椎体剔除(裁剪)是在裁剪空间中执行的,就在除以 wc, 之前。裁剪坐标 xc, yc, zc ,和 wc 比较。如果任何一个裁剪坐标小于 -wc ,或者大于 wc ,那么这个顶点就将被丢弃。
![](https://img.haomeiwen.com/i263545/e95b65393ab4e385.png)
然后,OpenGL 会在裁剪的地方重新构建多边形的边界。
透视投影
![](https://img.haomeiwen.com/i263545/ab16a53aa131f33e.png)
在透视投影中,截头椎体中的一个 3D 点(视点坐标),被映射到一个立方体中(NDC);x 坐标的范围从 [l, r] 映射到 [-1, 1] ,y 坐标从 [b, t] 到 [-1, 1] ,z 坐标从 [n, f] 到 [-1, 1] 。
注意,视点坐标是右手坐标系,但是 NDC 是左手坐标系。就是说,在视点空间中,位于原点的摄像机,是看向 -Z 轴方向的,但在 NDC 中是看向 +Z 轴方向的。既然 glFrustum() 对 near 和 far 距离只接收正值,我们需要在构建 GL_PROJECTION 矩阵时将它们取反。
OpenGL 中,视点空间中的 3D 点被投影到近平面(投影平面)上。下面这个图片展示了视点空间中的一个点 (xe,ye,ze)是如何投影到近平面上的 (xp,yp,zp)的。
![](https://img.haomeiwen.com/i263545/b40fb4b526751c26.png)
![](https://img.haomeiwen.com/i263545/35d00c77e83ea9e5.png)
在截头椎体顶视图中,视点空间中的 x 坐标,xe被映射到 xp,这是根据相似三角形计算得到的。
![](https://img.haomeiwen.com/i263545/527cc5662e500e02.png)
在截头椎体侧视图中,yp也是通过相似的方法计算的。
![](https://img.haomeiwen.com/i263545/9454acd92d53166f.png)
注意,xp 和 yp 都取决于 ze;它们和 -ze 成反比。就是说,他们都除以 -ze。这是我们构建 GL_PROJECTION 矩阵的第一条线索。视点坐标乘以 GL_PROJECTION 后,裁剪坐标仍然是一个齐次坐标。它通过除以裁剪坐标的 w 分量最终成为标准化设备坐标(NDC)。(可以在 OpenGL Transformation 上查看更多细节)
![](https://img.haomeiwen.com/i263545/bb1f5ea31013d201.png)
![](https://img.haomeiwen.com/i263545/7142a14ec803f691.png)
因此,我们可以设置齐次坐标的 w 分量为 -ze 。这样,GL_PROJECTION 矩阵的第四行成为了 (0, 0, -1, 0)。
![](https://img.haomeiwen.com/i263545/f7716dddddd647b4.png)
下一步,我们通过一个线性关系将 xp 和 yp 映射到 xn 和 yn; [l, r] ⇒ [-1, 1] 和 [b, t] ⇒ [-1, 1] 。
![](https://img.haomeiwen.com/i263545/6e83f4c4c7f5e87f.png)
![](https://img.haomeiwen.com/i263545/df55bfc4faf1872f.png)
![](https://img.haomeiwen.com/i263545/5752418cda96408d.png)
![](https://img.haomeiwen.com/i263545/2c14e01f663754d7.png)
然后,我们将 xp 和 yp 代入上面的方程式。
![](https://img.haomeiwen.com/i263545/2fce8cce6a6ac2d8.png)
![](https://img.haomeiwen.com/i263545/0dd461ddb3e81008.png)
注意我们将每个方程式的项的都除以 -ze 以进行透视除法 (xc/wc, yc/wc)。我们前面已经将 wc 设为 -ze 了,所以括号里的项成为了裁剪坐标的 xc 和 yc 。
我们可以从这些方程式里找到 GL_PROJECTION 矩阵的第 1 行和第 2 行。
![](https://img.haomeiwen.com/i263545/1835a916dc1b25ba.png)
现在我们只有 GL_PROJECTION 矩阵的第 3 行需要求解了。找到 zn 和其它的是有些不同的,因为在视点空间中, ze 永远都投影到近平面 -n 上。但我们需要一个唯一的 z 值来进行裁剪和深度测试。并且,我们应该可以进行反投影(逆变换)。既然我们知道 z 和 x 或 y 值无关,我们借用 w 分量来找到 zn 和 ze 之间的关系。所以,我们可以像这样指定 GL_PROJECTION 矩阵的第三行。
![](https://img.haomeiwen.com/i263545/321175e2d750c55e.png)
在视点空间中, we 的值是 1。因此,方程式成为了这样;
![](https://img.haomeiwen.com/i263545/8800cef570415438.png)
我们使用 (ze, zn) 的关系: (-n, -1) 和 (-f, 1) 来寻找系数 A 和 B,将它们代入上面的方程式中。
![](https://img.haomeiwen.com/i263545/7b97dcf7b08ab1ff.png)
为了解 A 和 B 的方程式,为 B 重写方程式 (1):
![](https://img.haomeiwen.com/i263545/6ebd2ba4da389bc1.png)
将方程式 (1') 代入方程式 (2) ,然后求解 A;
![](https://img.haomeiwen.com/i263545/b9a26fd63fe5cf37.png)
将 A 代入方程式 (1) 求解 B;
![](https://img.haomeiwen.com/i263545/f4e18f1bd59d1383.png)
我们找到了 A 和 B 。因此, ze 和 zn 之间的关系成为了:
![](https://img.haomeiwen.com/i263545/9f52269befbe7eaa.png)
最终,我们建立了整个 GL_PROJECTION 矩阵。完整的投影矩阵是:
![](https://img.haomeiwen.com/i263545/bbd289d2a11a4eee.png)
这个投影矩阵是用于一般的截头椎体。如果视椎是对称的,也就是说
![](https://img.haomeiwen.com/i263545/6dad7145023e266a.png)
和
![](https://img.haomeiwen.com/i263545/e896a135b08d6132.png)
那么它可以被简化为
![](https://img.haomeiwen.com/i263545/c5b40325896e42c2.png)
在我们继续之前,请看一下 ze 和 zn 之间的关系,方程式 (3) 。你知道它是一个非线性的关系。它意味着在近平面的精度很高,但是在远平面的精度很低。如果范围 [-n, -f] 变大,将会导致一个深度精度的问题(z-fighting);在远平面周围的 ze 的微小改变不会影响到 zn 的值。为了尽量减少深度缓存精度的问题,n 和 f 之间的距离应该尽可能的小。
![](https://img.haomeiwen.com/i263545/d6cf1fc0cb927c96.png)
正交投影
![](https://img.haomeiwen.com/i263545/99b1db79458816d3.png)
为正交投影构建 GL_PROJECTION 矩阵比透视模式简单多了。
视点空间中的 xe, ye 和 ze 分量都被线性映射到 NDC。我们只需要缩放矩形体积到立方体,然后将它移动到原点。让我们使用线性关系来找到 GL_PROJECTION 的元素。
![](https://img.haomeiwen.com/i263545/9607afb56f7edf89.png)
![](https://img.haomeiwen.com/i263545/86a36b84b208fe49.png)
![](https://img.haomeiwen.com/i263545/571705026248d20d.png)
![](https://img.haomeiwen.com/i263545/27a45dab12dbdb3e.png)
![](https://img.haomeiwen.com/i263545/e2b684712538eeec.png)
![](https://img.haomeiwen.com/i263545/dec4bdd249deb02c.png)
因为正交投影是不需要 w 分量的,所以 GL_PROJECTION 矩阵的第四行保持为 (0, 0, 0, 1)。因此正交投影完整的 GL_PROJECTION 矩阵为:
![](https://img.haomeiwen.com/i263545/76b9bbd873c152af.png)
如果视椎是对称的话,它可以更加简单, r = -l 和 t = -b 。
.
![](https://img.haomeiwen.com/i263545/a30c4bff87079baf.png)