1.从2D到3D:
到目前为止,我们一直都在使用一个2D平面,而且甚至是在3D空间里!所以,让我们大胆地拓展我们的2D平面为一个3D立方体。要想渲染一个立方体,我们一共需要36个顶点(6个面 x 每个面有2个三角形组成 x 每个三角形有3个顶点)
我们将原来的顶点数据:
修改为:
然后我们使用glDrawArrays来绘制立方体,但这一次总共有36个顶点。
如果操作无误,效果应该是:
这确实是一个立方体,但是立方体某些本应该被遮挡的面被绘制在了这个立方体其他面之上。这是因为OpenGL在绘制时是以一个三角形一个三角形地来绘制你的立方体,所以即便之前那里有东西他也会覆盖之前的像素。
为了解决这个问题,我们就不得不了解一个叫做Z缓冲的缓冲。
2.Z缓冲
OpenGL存储它的所有深度信息于一个Z缓冲(Z-buffer)中,也被称为深度缓冲(Depth Buffer)。GLFW会自动为你生成这样一个缓冲(就像它也有一个颜色缓冲来存储输出图像的颜色)。深度值存储在每个片段里面(作为片段的z值),当片段想要输出它的颜色时,OpenGL会将它的深度值和z缓冲进行比较,如果当前的片段在其它片段之后,它将会被丢弃,否则将会覆盖。这个过程称为深度测试(Depth Testing),它是由OpenGL自动完成的。
我们可以使用glEnable函数来开启深度测试:
glEnable(GL_DEPTH_TEST);
因为我们使用了深度测试,我们也想要在每次渲染迭代之前清除深度缓冲(否则前一帧的深度信息仍然保存在缓冲中)。就像清除颜色缓冲一样,我们可以通过在glClear函数中指定DEPTH_BUFFER_BIT位来清除深度缓冲:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
现在就达到了我们想要的效果:
3.绘制更多的立方体
如果我们想在屏幕上显示10个立方体。每个立方体看起来都是一样的,区别在于它们在世界的位置及旋转角度不同。立方体的图形布局已经定义好了,所以当渲染更多物体的时候我们不需要改变我们的缓冲数组和属性数组,我们唯一需要做的只是改变每个对象的模型矩阵来将立方体变换到世界坐标系中。
首先,让我们为每个立方体定义一个位移向量来指定它在世界空间的位置。我们将在一个glm::vec3数组中定义10个立方体位置:
接着,我们只需要调用十次glDrawArrays,只是我们在渲染之前每次传入一个不同的模型矩阵到顶点着色器中就可以了。需要注意的是我们可以对每个箱子加一点旋转。
glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++) {
glm::mat4 model;
model = glm::translate(model, cubePositions[i]);
float angle = 20.0f * i;
model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
ourShader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
最终,我们就可以得到以下的效果: