一个3D点(x,y,z)实际上被表示为4D的齐次点(x,y,z,1),因此我们可以使用4x4矩阵来表示所有所需的变换——具体来说,这允许平移和透视投影。
然后使用4x4的模型视图投影(Model-View-Projection,MVP)矩阵将该点转换为(x',y',z',w')。
模型矩阵将点从其局部建模坐标转换到世界坐标。非常有用,可以实例化单个对象的多个版本。
视图矩阵将该点转换为相机坐标系。在这个坐标系中,相机实际上是朝向-z轴方向看。
投影矩阵将该点转换为裁剪坐标,并最终定义特定的投影方式(例如正交或透视)。
MVP矩阵就是这三个矩阵相乘形成的组合变换。
对于处于裁剪坐标中的点而言,如果它投影后位于裁剪空间内,则它才会可见。裁剪空间是一个轴对齐的立方体,其角落点为(-1,-1,-1)和(+1,+1,+1)。通过除以w'来投影点,即(x',y',z',w')映射为(x'/w', y'/w', z'/w')。如果这三个值都在区间[-1,+1]内,则它可见,我们称该点位于标准化设备坐标中。这种除法是透视投影产生透视效果的原因。
有趣的部分在于,裁剪实际上发生在4D中(在进行透视划分之前)。因此,为了使点可见,x'、y'、z'的大小必须小于w'的大小。如果点经过裁剪,则进行透视划分。
最后,一个存活下来的点被映射到实际的设备坐标中,通过视口变换。这里有一个实际的设备坐标(x,y),它是帧缓冲区中的2D地址,而z是深度缓冲区范围内的深度值。然后将该点发送到光栅化器。
总之,OpenGL管道的第一部分如下所示:
- 在顶点着色器中,每个顶点 (x,y,z,1) 乘以 MVP 矩阵得到裁剪坐标 (x',y',z',w')。
- 顶点被组装成给定的基本图元(例如三角形)。
- 基本图元在四维空间中进行剪裁。
- 透视除法将点转换为 归一化设备坐标。
- 视口变换将点映射到设备坐标并发送给光栅化器。
建模变换是仿射变换,可以用如下矩阵表示:
_ _
| a b c d |
M = | e f g h |
| i j k l |
| 0 0 0 1 |
- -
一个点P = (x,y,z,1) 实际上被视为列向量[x y z 1]的转置,并通过右乘P' = M P进行变换(至少在概念上是这样的——硬件可能会转置所有内容,但仍然是同一件事)。通过仿射变换,我可以缩放、旋转、剪切、平移...任何保持平行线的东西。请注意,M的最后一行是恒等的——这很重要,因为我们不希望M干扰w坐标——我们将在投影矩阵中处理它。
请注意,我们可以使用w = 0来表示方向向量(例如,表面法线),这有效地忽略了平移分量。实际上,为了正确地变换表面法线,我们使用M上部3x3矩阵的逆转置所关联的法线矩阵:
_ _ ^ -T
| a b c |
N = | e f g |
| i j k |
- -
这样可以保持法线与它们各自平面的正交性。
例如,下面是一个矩阵,它通过s均匀缩放对象,逆时针绕z轴旋转theta,并将结果平移(4,5,6):
_ _
| s * cos(theta) -s * sin(theta) 0 4 |
| s * sin(theta) s * cos(theta) 0 5 |
| 0 0 1 6 |
| 0 0 0 1 |
- -
视图变换V是一种刚性变换,它只是一个旋转和平移,可以改变坐标系,使摄像机位于原点并“向外看”-z轴。
投影矩阵P在透视投影方面有些奇怪。在这里,我们必须将视锥体扭曲成规范裁剪立方体。这不是一个仿射变换。这就是最终乘以1/w的好处所在。这里使用以下形式的矩阵:
P = | a 0 0 0 |
| 0 b 0 0 |
| 0 0 c d |
| 0 0 -1 0 |
请注意,最后一行
不是身份矩阵。点 (x,y,z,1) 被映射为 (a*x, b*y, c*z + d, -z)。之后,在透视除法后,得到的结果为:
(-a*x/z, -b*y/z, -(c*z + d)/z)
注意到除以 -z 的部分吗?这就是透视缩短的来源。因为被裁剪的顶点将会有 z < 0,所以这实际上是一个正除法。
无论如何,我就说到这里了....