OpenGL基础14:摄像机

很可惜,openGL没有摄像机的概念

一、摄像机

我们当然需要想办法模拟出一个摄像机,也就是玩家的视角,在前面这一章:第一个正方体,我们仅将“摄像机”往z轴正方向移动了3个单位,并且正对着(0, 0, 0),但是大部分的情况下,类似于我们处于一个3D场景中,摄像机的视角都是从上斜向下的,这个时候若想要计算摄像机的位置和方向信息,就是需要一定的运算了

既然摄像机如此重要,我们当然需要知道它的很多信息,包括:

  1. 摄像机位置
  2. 观察方向(摄像机z轴)
  3. 垂直于观察方向,指向摄像机右侧的向量(摄像机x轴)
  4. 垂直于观察方向,指向摄像机正上方的向量(摄像机y轴)

对于①:必定是你来确定,又或者由输入确定(通过移动鼠标调整视角等),对于②:只要将摄像机的坐标减去焦点的坐标就可以了,在游戏中,这个焦点往往是玩家自己的世界坐标,而这里我们就当作是原点(经过如此计算得出来的这个向量理论是摄像机z轴的负方向

1
2
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);

对于③④,如果你对施密特正交化比较了解,那么应该就很容易通过公式得出对应的向量,不了解也没有关系,想要得到③的向量,我们只需要将y轴正方向向量(0, 1, 0)和摄像机z轴向量叉乘就可以,这个向量必定同时垂直于前两者,至于是x轴正方向还是负方向取决与叉乘的顺序,可以用右手定则得出,④同理

1
2
3
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));
glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);

二、LookAt矩阵

有了上面的所有信息后,我们就可以计算出摄像机的观察矩阵了

可以发现,上面的②③④向量相互垂直,它们构成了一个全新的向量空间,这样的话,我们只需要再有一个平移向量就可以轻松的创建一个矩阵,后面对于每一个顶点,我们只需要用这个矩阵乘以它就能将其变换到对应观察空间,这便是LootAt矩阵:

\operatorname{Look} A t=\left[\begin{array}{cccc} 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] *\left[\begin{array}{cccc} 1 & 0 & 0 & -P_{x} \\ 0 & 1 & 0 & -P_{y} \\ 0 & 0 & 1 & -P_{z} \\ 0 & 0 & 0 & 1 \end{array}\right]

其中 R 为右向量,U 为上向量,D 为方向向量,P 为摄像机位置向量

  • 对于位置向量要取反,这个在上一章也提到过,因为我们文中讲述的都是摄像机的相对位置,而我们实际需要操作的却是物体的相对位置
1
2
3
4
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));

用如上的方法,就可以直接创建一个LookAt矩阵,其中传入的第一个参数为摄像机位置,第二个参数为目标位置,第三个参数为你所描述的世界空间的上向量(就是上面的(0, 1, 0)了)

三、测试

上一章我们通过让箱子自转来让我们能看到箱子所有的六个面,现在我们可以尝试让摄像机围绕着箱子转:

相对上一章的代码,改动如下:

1
2
3
4
5
6
7
8
9
10
11
12
glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(57.0f), glm::vec3(-0.5f, 1.0f, 0.0f));
view = glm::lookAt(glm::vec3(camX, 0.0f, camZ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
projection = glm::perspective(glm::radians(45.0f), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
GLint modelLoc = glGetUniformLocation(shaderYellow.Program, "model");
GLint viewLoc = glGetUniformLocation(shaderYellow.Program, "view");
GLint projLoc = glGetUniformLocation(shaderYellow.Program, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));

看上去貌似没有什么区别,但是这个物体其实是静止的,是我们观察者的位置在改变