前言
我们知道我们在屏幕上看见的画面其实都是二维的,那么是怎么做到把三维空间中所展示的内容显示到一个二维空间上呢?这里就需要我们的视图变换以及投影变换来实现了。
整个过程我们可以理解为生活中拍照片,例如我们想去一个摄影棚拍全家福,我们可以分如下三步:
- 首先我们可以先决定好站位问题,谁在前谁在后等等,这步操作就是我们前面学习的模型变换(Model Transformation)
- 然后我们得全家移步到摄影棚拍(即把摄影棚的相机当做坐标原点),然后还要摆正相机(相机自身的旋转),把相机镜头朝向我们(相机的朝向)以及相机要和我们保持一定的距离(与相机的距离)才能拍出好的照片。这步操作就是我们本章要学习的视图变换(View/Camera Transformation)
- 最后就是按下相机的快门,拍出照片(照片就是二维的),这步操作也是我们本章要学习的投影变换(Projection Transformation)
上诉这些变换操作就是我们常说的MVP变换,它们对应的矩阵即MVP矩阵。
视图变换(View/Camera transformation)
平时大家一定也都拍过照片,想要拍出想要的照片,我们肯定要选好拍照的位置,对准要拍摄的物体,以及拿好相机(因为可以横着拍,竖着拍或者斜着拍)。在图形学中,也是一样的道理,我们首先需要定义摄像机如下几个属性:
- 位置(Position):即Camera的坐标,我们可以标记为
- 朝向(LookAt):即Camera往哪看,其实也就是相机的-z轴向量,我们可以标记为
(单位向量)
- Y轴的朝向(UpDirection):即Camera的y轴向量,我们可以标记为
(单位向量)。例如若Camera的y轴和世界坐标系的y轴平行,则看见的画面是正的,若Camera的y轴绕z轴旋转,则看见的画面也会跟着旋转。
注:我们约定在右手坐标系中,Camera的LookAt方向为自身的-z轴方向(例如3DMax中的Camera),而在左手坐标系中LookAt方向为自身的z轴方向(例如Unity中的Camera)。
通过相对运动我们知道,如果摄像机所观察的物体和摄像机一起运动,那么摄像机所观察到的画面是保持不变的,例如下图中,两个摄像机的位置朝向都不相同,看见的画面确是一样的。
那么假如有个变换矩阵 ,使我们的摄像机变换为位置在世界坐标原点,看向-z方向,摄像机的y轴即为世界坐标y轴。那么摄像机所观察的物体同样使用矩阵
进行变换,即可保持所观察到的画面不变。这样的操作就是我们所谓的视图变换,变换矩阵
即为视图变换矩阵。至于为什么要执行这样的一步操作,是为了使后续在执行投影变换时,简化计算。
接下来我们来看看矩阵 的值应该如何计算,根据前面的描述我们可以把视图变换拆分为如下几步:
- 通过平移变换,将Camera移到原点位置,即使
变为 (0,0,0)
- 通过旋转变换,使Camera看向-z方向且自身的y轴与世界坐标的y轴重叠,即使
变为 (0,0,-1),
变为 (0,1,0)
通过复合变换我们可知,只需要把上面两部所对应的矩阵相乘得到的结果及时我们的视图变化矩阵 的值。
首先是平移变换的矩阵,这个很简单,设 ,我们只需要移动
即可将摄像机移动到原点,对应矩阵(设为
)即为:
移动到原点后,我们要通过旋转,使 变为 (0,0,-1) ,
变为 (0,1,0)。这一步我们有点无从下手,因为我们之前讨论旋转矩阵时都是绕什么什么轴旋转多少多少度,而在这里我们并不知道应该绕什么轴旋转多少度。
那么应该如何解决呢?这里有一个逆向思维,即我们不知道如何将 变为 (0,0,-1) ,
变为 (0,1,0),但是我们能够知道如何将 (0,0,-1) 变为
,(0,1,0) 变为
,也就是上诉旋转的逆变换,我们假设我们原先要求的矩阵为
,那么它的逆变换矩阵即为
。
因为通过前面的知识我们知道,旋转矩阵中的值即为x,y,z三个轴的单位向量旋转后的值。在这里 (0,1,0) 变为 ,(0,0,1) 变为了
,因为
代表的是摄像机自身的-z轴方向,
代表的是摄像机自身的y轴方向,根据叉乘