OpenGL学习脚印: 投影矩阵和视口变换矩阵(math-projection and viewport matrix)


写在前面
前面几节分别介绍了模型变换,视变换,本节继续学习OpenGL坐标变换过程中的投影变换。这里主要是从数学角度推导投影矩阵。对数学不感兴趣的,可以稍微了解下,或者跳过本节内容。

本文主要翻译并整理自 songho OpenGL Projection Matrix一文,这里对他的推导思路稍微进行了整理。

通过本节可以了解到

  • 透视投影矩阵的推导
  • 正交投影矩阵的 推导
  • 视口变换矩阵的推导
  • zFighting问题

投影变换

OpenGL最终的渲染设备是2D的,我们需要将3D表示的场景转换为最终的2D形式,前面使用模型变换和视变换将物体坐标转换到照相机坐标系后,需要进行投影变换,将坐标从相机—》裁剪坐标系,经过透视除法后,变换到规范化设备坐标系(NDC),最后进行视口变换后,3D坐标才变换到屏幕上的2D坐标,这个过程如下图所示:

android开发Opengl ES之矩阵变换平移 opengl投影矩阵变换_模型变换

投影变换通过指定视见体(viewing frustum)来决定场景中哪些物体将可能会呈现在屏幕上。在视见体中的物体会出现在投影*面上,而在视见体之外的物体不会出现在投影*面上。投影包括很多类型,OpenGL中主要考虑透视投影(perspective projection)和正交投影( orthographic projection)。两者之间存在很大的区别,如下图所示(图片来自Modern OpenGL):

android开发Opengl ES之矩阵变换平移 opengl投影矩阵变换_投影矩阵_02

上面的图中,红色和黄色球在视见体内,因而呈现在投影*面上,而绿色球在视见体外,没有在投影*面上成像。

指定视见体通过(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal)6个参数来指定。注意在相机坐标系下,相机指向-z轴,nearVal和farVal表示的剪裁*面分别为:*裁剪*面z=−nearValz=−nearVal。推导投影矩阵,就要利用这6个参数。在OpenGL中成像是在*裁剪*面上完成。

透视投影矩阵的推导

透视投影中,相机坐标系中点被映射到一个标准立方体中,即规范化设备坐标系中,其中[l,r]映射到[−1,1][l,r]映射到[−1,1],如下图所示:

android开发Opengl ES之矩阵变换平移 opengl投影矩阵变换_模型变换_03

注意到上面的相机坐标系为右手系,而NDC中+z轴向内,为左手系。

我们的目标

求出投影矩阵的目标就是要找到一个透视投影矩阵P使得下式成立:

⎡⎣⎢⎢⎢xcyczcwc⎤⎦⎥⎥⎥=P∗⎡⎣⎢⎢⎢xeyezewe⎤⎦⎥⎥⎥[xcyczcwc]=P∗[xeyezewe]

过程被称为透视除法。要找到我们需要的矩阵P,我们需要利用两个关系:

  • 投影位置xpxp

计算投影*面上的位置

投影时原先位于相机坐标系中的点p=(xe,ye,ze)p=(xe,ye,ze)。具体过程如下图所示:

android开发Opengl ES之矩阵变换平移 opengl投影矩阵变换_投影变换_04

需要空间想象一下,可以得出左边的图是俯视图,右边是侧视图。
利用三角形的相似性,通过俯视图可以计算得到:
xpxe=−nzexpxe=−nze

由(1)(2)这个式子可以发现,他们都除以了−ze−ze。

其中xpxp的映射关系如下图所示:

android开发Opengl ES之矩阵变换平移 opengl投影矩阵变换_投影变换_05

则可以得到xpxp

由式子6可以得到:
xc=2nr−lxe+r+lr−l∗ze(1.6)(1.6)xc=2nr−lxe+r+lr−l∗ze

对于ypyp