一个关于坐标转换系统的讲解
一、摄像机
- OpenGL本身没有摄像机(Camera)的概念,但是我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生我们在移动的感觉,而不是场景在移动。
- 以摄像机的视角作为场景原点时场景中所有的顶点坐标:观察矩阵把所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。
- 要定义一个摄像机,我们需要它在世界空间中的位置、观察方向、一个指向它右侧的向量以及一个指向它上方的向量。
- 实际上创建一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。
- 摄像机位置:世界空间中一个指向摄像机位置的向量。
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
- 摄像机方向:指 摄像机指向哪个方向。现在我们让摄像机指向场景的原点:(0, 0, 0)。
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
2.1 此时,用场景原点向量减去摄像机位置向量的结果就是摄像机的指向方向。
2.2 由于我们知道摄像机指向z轴负方向,但是我们希望方向向量(Direction vector)指向摄像机的z轴正方向。如果我们交换相减的顺序,我们就会获得一个指向摄像机z轴正方向的向量。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轴向量(cameraRight)和z轴向量(cameraDirection), 获取一个指向摄像机的y轴正方向的向量就相对简单,直接利用叉乘。
glm::vec3 cameraUp = glm::normalize(glm::cross(cameraDirection, cameraRight);
二、 LookAt矩阵
- 使用矩阵的好处之一是如果你使用3个相互垂直(或非线性)的轴定义了一个坐标空间,你可以用这3个轴 外加 一个平移向量 来创建一个矩阵,并且你可以用这个矩阵乘以任何的向量来将其变换到当前这个坐标空间。
- 有了3个相互垂直的轴和一个定义摄像机空间的位置坐标,可以创建自己的LookAt矩阵。
- LookAt矩阵:创建一个看着给定目标的观察矩阵。
- 位置向量在矩阵中运算时是相反的,因为我们最终希望把世界平移到与我们自身移动的相反方向。
- glm::LookAt函数需要一个位置、目标和上向量。他会创建一个和在上一节使用的一样的观察矩阵。
glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), //摄像机的位置向量
glm::vec3(0.0f, 0.0f, 0.0f), //目标位置向量
glm::vec3(0.0f, 1.0f, 0.0f)); //上向量
- 摄像机类里面的返回矩阵定义如下:
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
//这里的目标位置改成了 摄像机位置 加上 上方向向量,
//glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f)
//这样可以保证摄像机永远注视着目标方向。
三、视角移动
- 为了能够改变视角,我们需要根据鼠标的输入改变 cameraFront 向量。
- 欧拉角: 俯视角(Pitch)、偏航角(Yaw)和滚转角(Roll)
2.1 俯视角(Pitch):往上或者往下看的角度,绕x轴。
2.2 偏航角(Yaw):往左或者往右看的程度,绕y轴。
2.3 滚转角(Roll):如何翻转摄像机,绕z轴。