OpenGL本身没有摄像机(Camera)的概念,但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动。
摄像机/观察空间
当我们讨论摄像机/观察空间(Camera/View Space)的时候,是在讨论以摄像机的视角作为场景原点时场景中所有的顶点坐标:观察矩阵把所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。要定义一个摄像机,我们需要它在世界空间中的位置、观察的方向、一个指向它右测的向量以及一个指向它上方的向量。细心的读者可能已经注意到我们实际上创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。
摄像机位置
其中,摄像机的位置最为简单,给它一个世界坐标即可,像这样
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
摄像机方向
摄像机方向这里有点绕,在OpenGL中使用的是右手坐标系,z轴的正方向是从屏幕指向你的,如果我们希望摄像机向后移动,我们就沿着z轴的正方向移动。
假设物体的正面朝向z轴正方向,我们想用摄像机观察这个物体的正面,需要将摄像机放在物体所在位置沿着z轴正方向平移一段距离,并让摄像机的朝向指向物体的正面。由于我们使用的是右手坐标系,所以摄像机的z轴的正方向应该在摄像机的后面。
根据向量减法,如果我们要求摄像机指向目标的方向向量,需要用目标的位置向量减去摄像机的位置向量。但是这个指向向量的方向是与摄像机z轴的方向相反,我们需要的是一个与z轴方向相同的向量作为方向向量来构建摄像机的观察坐标系,因此,需要用摄像机位置减去目标位置(注意看上图摄像机的蓝色轴)。
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);
右轴
我们需要的另一个向量是一个右向量(Right Vector),它代表摄像机空间的x轴的正方向。为获取右向量我们需要先使用一个小技巧:先定义一个上向量(Up Vector)。接下来把上向量和第二步得到的方向向量进行叉乘。两个向量叉乘的结果会同时垂直于两向量,因此我们会得到指向x轴正方向的那个向量(如果我们交换两个向量叉乘的顺序就会得到相反的指向x轴负方向的向量):
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));
上轴
现在我们已经有了x轴向量和z轴向量,获取一个指向摄像机的正y轴向量就相对简单了:我们把右向量和方向向量进行叉乘
glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);
Look At矩阵
前面我们为摄像机创建的空间坐标就是OpenGL学习笔记:坐标系统章节中提到的观察空间的坐标系。
现在观察空间有了,需要将世界空间坐标转换为观察空间坐标。
具体该怎么做呢?OpenGL中使用Look At矩阵来做这件事,先来看一下Look At矩阵:
L o o k A t = ( R x R y R z 0 U x U y U z 0 D x D y D z 0 0 0 0 1 ) ⋅ ( 1 0 0 − P x 0 1 0 − P y 0 0 1 − P z 0 0 0 1 ) LookAt=\left( \begin{array}{ccc} R_x & R_y & R_z & 0\\ U_x & U_y & U_z & 0\\ D_x & D_y & D_z & 0\\ 0& 0& 0& 1 \end{array} \right) \cdot \left( \begin{array}{ccc} 1& 0& 0& -P_x\\ 0& 1& 0& -P_y\\ 0& 0& 1& -P_z\\ 0& 0& 0& 1 \end{array} \right) LookAt=
RxUxDx0RyUyDy0RzUzDz00001
⋅
100001000010