在 OpenGL 中,通过矩阵,我们可以对一个物体进行很方便的 3D 变换,使得静态的物体活动起来。这里使用 GLM 来尝试给予一个立方体不同的变换,使其活动起来
🚀 代码: Github
🔗 参考链接:https://learnopengl.com/#!Getting-started/Transformations
画一个立方体
边长为 4, 中心位置为(0, 0, 0)。
分别启动和关闭深度测试 glEnable(GL_DEPTH_TEST) 、 glDisable(GL_DEPTH_TEST) ,查看区别,并分析原因。
画一个立方体比较简单,具体的步骤和之前画三角形差不多
这里使用两个三角形来构成立方体的一个面,具体的顶点数据如下
1 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, |
每一行的前三个就是顶点的坐标,后面两个是纹理的位置。可以注意到,其中有两个点是重复的,我们可以使用 EBO 来对其进行简化
删掉重复的点,将其简化为
1 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, |
并且添加上 EBO
1 | 0, 1, 2, |
立方体的其他面也同样的操作,最后得到$4*6 = 24$个顶点,以及$6 * 6 =36$个顶点索引
然后用之前的方法对其进行渲染,为了方便观察,这里给他加了一个图片的纹理
这时我们发现,无论是否开启Depth test
,都只是显示一个正方形,这是为什么捏?
原来我们是没有使用透视,使用GLM
为其加上两个矩阵,将摄像机后移并且加上透视
1 | glm::mat4 view = glm::mat4(1.0f); |
然后再对比一下Depth test
关闭 | 开启 |
---|---|
可以看到,当关闭了Depth test
之后,我们可以观察到侧边的四个面,并且不正常地覆盖了正面。这是因为 OpenGL 在渲染的时候并没有计算深度,把侧边的面画在了正面之上。
平移 Translation
使画好的 cube 沿着水平或垂直方向来回移动。
可以使用glm::translate
调整model
使图像平移
1 | if (this->en_translation) { // 平移 |
然后加上一些判断条件使其自动进行平移
1 | if (this->auto_translation) { |
效果图:
具体的效果就是立方体的位置会不停变化,当 x 或 y 遇到边界的时候就会逆转变化方向,形成上图的弹弹弹效果。
旋转(Rotation)
使画好的 cube 沿着 XoZ 平面的 x=z 轴持续旋转。
这个比较简单,只需要将旋转的x
和z
参数调成 1,那么立方体就会沿着 x=z 轴持续旋转。然后使用glfwGetTime
作为旋转角度,然后就可以呈现出秩序旋转的动画了。
1 | this->rotate = glm::vec3(1.0f, 0.0f, 1.0f); |
效果图如下:
放缩(Scaling)
使画好的 cube 持续放大缩小。
只需要使用gml::scale
,其参数分别是xyz
轴缩放的比例,然后加上一些判断条件,形成有规律的缩放动画。
1 | if (this->en_scale) { // 缩放 |
效果如下:
添加 GUI
GUI 和之前的也差不多,使用Checkbox
激活相应的变换以及动画,使用SliderFloat
手动调整或呈现变换的参数。
这样就可以同时控制多个变量,调整参数快速得到我们需要的结果
渲染管线
OpenGL 中的图形渲染管线(Graphics Pipeline
),是原始数据转换成屏幕上的像素的一个过程。
其主要可以分为两个部分,首先是将 3D 坐标转换成 2D 坐标,然后就将 2D 坐标转换成屏幕上的像素。
着色器(Shader
)就是在管线中的处理程序。着色器是运行在 GPU 的程序,支持并行化处理。我们平时用到的有顶点着色器和片段着色器。
在一个完整的渲染管线中,首先我们得顶点数据将会被送到顶点着色器,顶点着色器负责解析顶点数据。
就拿这次的实验的顶点着色器为例子
1 |
|
首先,将顶点数据分为两层(location),前三个为 3D 坐标,后两个为纹理位置。
然后有三个uniform
数据,是从 CPU 中传递进来的数据。
还有一个out
为输入到其他着色器的数据
GPU 会将传输进来的数据根据main
函数对其进行处理,输出gl_Position
和TexCoord
两个变量到下一个过程。
然后在片段着色器中对上个着色器输出的TexCoord
进行处理,并输出。
1 |
|
而在这两个着色器中间其实还有一些着色器,比如几何着色器。不过这个应该是通过glDrawArrays(GL_TRIANGLES, 0, 36);
选择相应的图元(如 GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP)生成的。
其一般的流程如下图:
而在我们这几次的实验中,只用到了顶点着色器和片段着色器以及指定一下图元。
将以上三种变换相结合
这里再弄一个立方体,使其围绕着中间的旋转
只需要使用glm
的函数对model
进行多个变换就可以了
1 | if (en_other) { |
具体效果如下:
当然可以添加上一开始弄得碰撞平移动画,把他叠加上面,就可以边公转边自转然后还能平移
这样看上去依旧又掉鬼畜,因此,我把摄像机向上移动了一点
1 | view = glm::lookAt(glm::vec3(0.0f, 2.0f, -6.0f), |
然后把他们的旋转参数调成(0.0f, 1.0f, 0.0f)
,使其绕着 y 轴旋转
然后再加上一个小的“月球”绕着地球旋转。
只需要在地球的基础上加上相同的参数即可
1 | if (en_other) { |
最终效果如下: