计算机图形学 | 实验六:旋转立方体
- 计算机图形学 | 实验六:旋转立方体
- Z-缓冲
- GLM函数库
- PVM矩阵
- PVM矩阵的使用
华中科技大学《计算机图形学》课程
MOOC地址:计算机图形学(HUST)
计算机图形学 | 实验六:旋转立方体
在正式搭建环境之前,我们先来介绍一下读完下面的部分你会了解些什么。
- 绘制出旋转立方体需要的新知识
- 认识一些 OpenGL的新功能
接下来,我们来介绍一下绘制旋转立方体。绘制效果如下:
Z-缓冲
Z-缓存(Z-Buffer):这是一项处理 3D物体深度信息的技术,它对不同物体和同一物体不同部分的当前 Z坐标进行纪录,也就是在 3D环境中,每个像素会利用一组数据资料来定义像素在显示时的纵深度(即 Z轴坐标值)。在进行着色时,对那些在其他物体背后的结构进行消隐,使它们不被显示出来。
在 OpenGL 中,坐标映射到屏幕屏幕空间后,其 z值即最终 z-缓冲的值,只需记录每个屏幕像素点的 z 值,并与当前绘制的片元的 z值进行比对,即可判断物体是否遮挡或被遮挡。实际应用中,我们只需要开启深度测试即可。
glEnable(GL_DEPTH_TEST);
GLM函数库
GLM是 OpenGL Mathematics的缩写,它是一个只有头文件的库,也就是说我们只需包含对应的头文件就行了,不用链接和编译。GLM可以在它们的网站上下载。把头文件的根目录复制到你的 includes文件夹,就可以使用这个库了。使用这个库,好处就在于我们只需要输入特定参数,就可以生成我们需要的矩阵。
PVM矩阵
PVM矩阵即 P:projection;V:view;M:model。其中,model矩阵对应从局部坐标系到世界坐标系的变换,模型矩阵是一种转换矩阵,它能通过对对象进行平移、缩放、旋转来将它置于它本应该在的位置或方向。
view矩阵对应从世界坐标系到观察坐标系的变换,观察坐标系就是从摄像机的角度观察到的坐标系。而这通常是由一系列的平移和旋转的组合来平移和旋转场景从而使得特定的对象被转换到摄像机前面。
projection 矩阵对应从观察坐标系到剪裁空间的变换,它指定了坐标的范围,例如,每个维度都是从-1000到 1000。投影矩阵接着会将在它指定的范围内的坐标转换到标准化设备坐标系中(-1.0,1.0)。所有在在范围(-1.0,1.0)外的坐标都不会被绘制出来并且会被裁剪。由投影矩阵创建的观察区域被称为平截头体,且每个出现在平截头体范围内的坐标都会最终出现在用户的屏幕上。将一定范围内的坐标转化到标准化设备坐标系的过程(而且它很容易被映射到 2D观察空间坐标)被称之为投影,因为使用投影矩阵能将 3维坐标投影到很容易映射的2D标准化设备坐标系中。
一旦所有顶点被转换到裁剪空间,最终的操作——透视划分将会执行,在这个过程中我们将位置向量的 x,y,z分量分别除以向量的齐次 w分量;透视划分是将 4维裁剪空间坐标转换为 3维标准化设备坐标。这一步会在每一个顶点着色器运行的最后被自动执行。 在这一阶段之后,坐标经过转换的结果将会被映射到屏幕空间(由glViewport设置)且被转换成片段。
PVM矩阵的使用
那么如何在实践中使用 pvm矩阵呢,首先我们需要引入 GLM函数库的头文件,我们所需要用到的功能都在这三个头文件中。
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
然后我们需要设置 view矩阵的相关参数。
glm::vec3 camera_position = glm::vec3(0.0f, 0.0f, 3.0f); // 摄像机位置
=glm::vec3 camera_front = glm::vec3(0.0f, 0.0f, -1.0f); // 摄像机方向
glm::vec3 camera_up = glm::vec3(0.0f, 1.0f, 0.0f); // 摄像机上向量
然后我们需要设置 projection矩阵的视野 fov:
float fov = 45.0f;
进入主循环之后我们先计算 model矩阵,首先我们需要创建一个 model矩阵,然后 glm:translate函数是进行平移变换的矩阵,将物体平移(0.0,0.0,0.0)位置,然后进行旋转,第二个参数为旋转的角度,第三个参数为旋转轴。最后进行缩放 glm::vec3变量的三个值分别代表 x,y,z方向的缩放比例。
glm::mat4 model(1);//model矩阵,局部坐标变换至世界坐标
model = glm::translate(model, glm::vec3(0.0,0.0,0.0));
model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));
model = glm::scale(model, glm::vec3(1.0f,1.0f,1.0f));
为了得到我们需要的 view矩阵,我们需要先创建一个 mat4 view矩阵,通过 glm中的 rotate函数去计算,第一个是相机的位置,第二个参数是相机所正对的目标的坐标,这里使用 camera_position+camera_front(相机的方向),进行向量的加法之后可以获得相机正对的坐标,第三个参数是相机的上向量。
glm::mat4 view(1);//view矩阵,世界坐标变换至观察坐标系
view = glm::lookAt(camera_position, camera_position + camera_front, camera_up);
在 GLM中可以这样创建一个透视投影矩阵,它的第一个参数定义了 fov的值,它表示的是视野,并且设置了观察空间的大小。对于一个真实的观察效果,它的值经常设置为 45.0,但想要看到更多结果你可以设置一个更大的值。第二个参数设置了宽高比,由视口的高除以宽。第三和第四个参数设置了平截头体的近和远平面。我们经常设置近距离为 0.1而远距离设为 100.0。所有在近平面和远平面的顶点且处于平截头体内的顶点都会被渲染。
glm::mat4 projection(1);//projection矩阵,投影矩阵
projection = glm::perspective(glm::radians(fov), (float)screen_width / screen_height, 0.1f, 100.0f);
我们之前的操作都只是得到 model,view和 projection矩阵,但是不能忘记将这三个矩阵传入到着色器,否则着色器是没有办法使用的。
glGetUniformLocation 可以获得某个着色器中参数的位置,第一个参数为着色器 id,第二个参数为该参数的名称。
int model_location = glGetUniformLocation(shader.ID, "model");
// 获取着色器内某个参数的位置
glUniformMatrix4fv是向指定位置传入一个 4X4的矩阵值。通过这两个函数我们将 pvm矩阵传入着色器中。
glUniformMatrix4fv(model_location, 1, GL_FALSE, glm::value_ptr(model));// 写入参数值
最后,在着色器中,对于矩阵的乘法,由于不符合乘法交换律,所以我们应当注意相乘顺序。
gl_Position = projection * view * model * vec4(aPos, 1.0);
整个绘制旋转立方体的程序的新知识点介绍就到此为止,效果如下: