OpenGL透视矩阵构造法
(本文未经许可禁止转载)
背景
OpenGL的渲染视窗默认是不提供任何透视变形的,也就是说如果我们画一块立方体上去,那么它渲染出来的楞在屏幕上是相互平行的(下图左边),没有透视关系。但我们往往想要的是下面这张图右面的效果:
思路
为了解决这个问题,我们常用的手法是:在视窗中把所有的顶点“变形”,让它们看上去有透视的感觉即可。也就是,我们所有的工作其实本质上还是在正交视图里做,只不过是把所有的几何体都变形了,让它们从特定角度上来看是存在透视关系的。
用上面的图来描述的话,就是把左面灰色区域中所有的点,都通过一个3维空间Affine仿射变换矩阵(4x4
)映射到上图右面的方盒里。再由OpenGL
负责将仿射空间的几何体还原到欧式空间中。
形象的例子
根据学习这部分知识的时候,曾经听过了来自UC Davis Academics
的一位老爷子的公开课,这课里面就有一个非常形象的例子
左上角就是我们放置的立方体,而经过透视变换矩阵及还原欧式空间后,立方体就变形成老爷子指的地方了(视频连接)。
这里注意: 经过我后来的思考发现,其实这种变换存在一些“线渲染”的问题,因为我们映射的只是
vertex
信息,而连接点与点的折线则是在Fragment shading
时期完成的。 但是仔细想想不难发现,若现实中一根直线段,它横跨我们视野的时候,根据透视规律,是弯着的。为了优化“线”的表述,不难猜测工业级CG软件需要独立构造Affine
空间下的直线表述和图元。若是更加复杂的贝塞尔曲线则需要更高阶多项式的表述方式。
OpenGL实现
OpenGL Math
好在不需要我们关注这个神奇的4x4
的矩阵到底怎么构造的,它只要求我们提供一些参数就能构造一块透视矩阵:
glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);
- 第一个参数:FOV,一个弧度值,详情见图;
- 第二个参数:摄像机视窗横纵比;
- 第三个参数:近平面距离;
- 第四个参数:远平面距离。
具体实现
在讨论具体实现前需要注意:
- 我们在进行透视变换前,要求所有的点都已经转换成摄像机空间下的坐标了。
- 所有的矩阵都是列空间构造。
- 矩阵不能含有输入位置的任何信息,所以我们通过把
z
转移到w
来间接实现近大远小。
公式
M_{perspective} := \begin{pmatrix}
\frac{n}{r} & 0 & 0 & 0\\
0 & \frac{n}{t} & 0 &0 \\
0 & 0 & -\frac{f+n}{f-n} & -\frac{2fn}{f-n} \\
0 & 0 & -1 & 0
\end{pmatrix}
此时的参数:
- 这里,我们仅讨论视窗对称的情况,不讨论
VR
情形。 \lambda
为任意不为0
的实数。OpenGL
为我们算好的的矩阵一般以\lambda=1
为基准。
概览图
我们这章节完成了投射矩阵的学习。它在下列计算流水线的最后一步(M_{projection}:=M_{perspective}
)
\mathbf{v_{today}}=M_{projection}\cdot M_{camera}^{-1} \cdot M_{local} \cdot \mathbf{v_{local}}
后续工作
最后\mathbf{v_{today}}
要被整体除以w
坐标,得到的x,y
就是屏幕空间的信息,z
则被用来做深度测试
。