视图矩阵
在模型矩阵中,我们关心的是空间中的点在经历变换后在世界坐标系下的位置。事实上,我们更加关心空间中的点相对于观察者的位置。最简单的方案是将观察者置于原点处,面向z轴(或x轴、y轴)正半轴,那么空间中的点在世界坐标系下的位置就是其相对于观察者的位置。
观察者的位置和方向会变化,看上去就好像整个世界的位置和方向发生变化了一样,所以解决的方案很简单,将世界里的所有模型看作一个大模型,在所有模型矩阵的左侧再乘以一个表示整个世界变换的模型矩阵,就可以了。这个表示整个世界变换的矩阵又称为“视图矩阵”,因为他们经常一起工作,所以将视图矩阵乘以模型矩阵得到的矩阵称为“模型视图矩阵”。模型视图矩阵的作用是:乘以一个点坐标,获得一个新的点坐标,获得的点坐标表示点在世界里变换,观察者也变换后,点相对于观察者的位置。 视图矩阵同样也可以分为平移、旋转和缩放,视图矩阵是将观察者视为一个模型,获得的观察者在世界中变换的模型矩阵的逆矩阵。
观察者平移了(tx,ty,tz),视图矩阵如下,可以看出如果将视图矩阵看作整个世界的模型矩阵,相当于整个世界平移了(-tx,-ty,-tz)。
观察者绕z轴旋转了角度θ,视图矩阵如下,相当于整个世界绕z轴旋转了-θ度。
观察者在三个方向等比例缩小了s倍,视图矩阵如下,相当于整个世界放大了s倍。
观察者缩小的情形可能会引起困惑:如果人和猫咪的眼睛在同一个位置,人看到的世界和一只猫咪看到的世界应当是一样尺寸的,这和上述视图矩阵的情形矛盾;但是直觉告诉我,如果你喝了缩小药水,你应该会觉得整个世界在膨胀,就像视图矩阵所表现的那样。解答是这样:如果在计算机上模拟观察者喝了缩小药水的情形,在屏幕上看到整个世界是膨胀的,因为在那个虚拟的三维空间中,计算机屏幕这个“窗口”也随你(观察者)缩小。
视图矩阵实际上就是整个世界的模型矩阵,这给我一点启发:一个模型可能由多个较小的子模型组成,模型自身有其模型矩阵,而子模型也有自己的局部模型矩阵。考虑一辆行驶中的汽车的轮胎,其模型视图矩阵是局部模型矩阵(描述轮胎的旋转)左乘汽车的模型矩阵(描述汽车的行驶)再左乘视图矩阵得到的。
投影矩阵
模型视图矩阵的作用是确定某一帧中,空间里每个顶点的坐标,而投影矩阵则将这些顶点坐标映射到二维的屏幕上,即:
最主要的有两种投影方式,正射投影和透视投影。前者用于精确制图,如工业零件侧视图或建筑物顶视图,从屏幕上就可以量测平行于屏幕的线段长度;后者用于模拟视觉,远处的物体看上去较小。下图中,空间中的同一个矩形,正射投影后仍然是矩形,而透视投影后则变成了梯形。 正射投影(投影面和相机空间):
透视投影(投影面和相机空间):
三维世界的显示中,屏幕模拟了一个窗口,你透过这个窗口观察“外面”的世界。你的屏幕是有边缘的(除非你有一个球形的房间,内壁全是屏幕),因此你仅仅能观察到那个世界的一部分,即“相机空间”。相机空间的左、右、上、下边界是受限于屏幕的边缘,同时也设定前、后边界,因为你很难看清太近或太远的东西。在正射投影中,相机空间是一个规则的立方体,而在透视投影中则是一个方台体。
三维模型可能在不同的显示器上展现,因此投影的过程中不该将显示器参数加入进来,而是将空间中的点投影到一个规范的显示器中。另外,透视投影中的z值并不是毫无用处,它可以用来表示顶点的“深度”:如果三维空间中的两个不同顶点投影到平面上时重合了,那么将显示深度较浅的顶点。
定义一个规范的视窗区域(CCV),为x,y,z都处在区间[-1,1]之间的边长为2的立方体。x和y坐标值用来线形拉伸到到实际屏幕上,而z值存储了“深度”。而投影的过程就是将三维空间中的点从相机空间映射到CCV中。
正射投影非常简单,直接将矩形的相机空间线形压缩到CCV中即可。采取顶视图,相机空间的左右边界为:
简单的线形成比例关系:
推广到y轴和z轴:
相机空间中的点经过正射投影矩阵左乘后得到的点都在CCV之中:
透视投影相对较为复杂,同样用顶视图考虑x坐标的情况:
转化为齐次的方式:
推广到y轴:
透视投影矩阵的第三行不是我们关心的内容,只要保证不同顶点投影前后的点坐标的第三个分量z和z’的大小关系不变就可以。
透视投影矩阵尾行是(0,0,1,0),这样就将计算得到的坐标的第四个分量赋值为z而不是1。将相机空间左乘投影矩阵后的结果不是一个CCV空间,如果你将这个空间画出来,会发现其仍然是一个方台形。这时进行“透视除法”,将上一步得到的点坐标化为第四个分量为1的标准齐次坐标: