透视投影的基本属性是将 x/y 坐标除以深度(距观察者的距离)。这使得离观察者较近的对象(具有较小的深度值)变大,而离观察者较远的对象(具有较大的深度值)变小。
下一个难题是同质坐标如何工作。由顶点着色器生成的同质空间中的 (x, y, z, w) 坐标通过除以 w 转换为常规 3D 坐标:
(x, y, z, w) --> (x/w, y/w, z/w, 1)
Run Code Online (Sandbox Code Playgroud)
所以我们想要除以深度来实现透视,并且我们知道顶点着色器产生的坐标将除以 w。为了得到想要的结果,我们可以简单地将眼睛坐标空间中的深度值放入w坐标中。
这正是投影矩阵最后一行的作用。最后一行与输入向量(即顶点的眼空间坐标)的点积产生输出的 w 值:
(0 0 -1 0) * (x y z 1) = -z
Run Code Online (Sandbox Code Playgroud)
您可能期望矩阵元素的值为 1,以便简单地将眼睛空间中的 z 值复制到顶点着色器输出的 w 值。我们使用-1来反转符号的原因是基于OpenGL中眼睛空间坐标的常见排列。
OpenGL 中的眼睛坐标通常将“相机”置于原点,向下看负z 轴。所以z坐标的可见范围有负值。由于我们希望在生成的 w 坐标中显示距观察者的距离,因此我们翻转眼睛空间 z 坐标的符号,这会将负 z 值转换为距原点的正距离值。
请注意,其中大部分只是常见策略,部分源于遗留的固定功能管道。通过当前 OpenGL 版本中使用的可编程管道,您可以完全自由地组织坐标空间和变换。例如,您可以轻松使用眼睛空间坐标系,其中相机指向正 z 方向,然后在投影矩阵的最后一行使用 1 而不是 -1。