企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 第四课:彩色立方体 # 第四课:彩色立方体 欢迎来到第四课!你将学到: - 画立方体,代替单调的三角形 - 加上绚丽的色彩 - 学习深度缓存(Z-Buffer) ## 画立方体 立方体有六个方形表面,而OpenGL只支持画三角形,因此需要画12个三角形,每面两个。我们用定义三角形顶点的方式来定义这些顶点。 ``` <pre class="calibre16">``` <span class="token2">// Our vertices. Tree consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.</span> <span class="token2">// A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices</span> static const GLfloat g_vertex_buffer_data<span class="token1">[</span><span class="token1">]</span> <span class="token">=</span> <span class="token1">{</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token2">// triangle 1 : begin</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token2">// triangle 1 : end</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token2">// triangle 2 : begin</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token2">// triangle 2 : end</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token1">,</span><span class="token">-</span><span class="token6">1.0</span>f<span class="token1">,</span> <span class="token6">1.0</span>f <span class="token1">}</span><span class="token1">;</span> ``` ``` OpenGL的缓冲区由一些标准的函数(glGenBuffers, glBindBuffer, glBufferData, glVertexAttribPointer)来创建、绑定、填充和配置;这些可参阅第二课。绘制的函数调用也没变,只需改绘制的点的个数: ``` <pre class="calibre16">``` <span class="token2">// Draw the triangle !</span> <span class="token3">glDrawArrays</span><span class="token1">(</span>GL_TRIANGLES<span class="token1">,</span> <span class="token6">0</span><span class="token1">,</span> <span class="token6">12</span><span class="token">*</span><span class="token6">3</span><span class="token1">)</span><span class="token1">;</span> <span class="token2">// 12*3 indices starting at 0 -> 12 triangles -> 6 squares</span> ``` ``` 这段代码,有几点要解释: - 现在为止,三维模型都是固定的:要改就要改源码,重新编译,然后祈望不会错。我们将在第七课中学习如何加载动态模型。 - 实际上,每个顶点至少被写了三次(在以上代码中搜索“-1.0f,-1.0f,-1.0f”看看)。这是可怕的内存浪费。我们将在第九课中学习怎样优化。 现在,你有了画一个白色立方体的所有必备条件。让着色器运行起来,至少试试吧:) ## 添加颜色 Adding colors 颜色,从概念上说,像极了位置:它就是数据。OpenGL中,它们都是“属性”。事实上,之前已在glEnableVertexAttribArray()和glVertexAttribPointer()用过属性设置了。现在我们加上颜色属性,代码很相似的。 首先,声明颜色:每个顶点一个RGB(红绿蓝)三元组。这里用随机的方式生成的,所以结果可能看起来不那么好;但你可以调整得更好,例如:把顶点的位置作为颜色值。 ``` <pre class="calibre16">``` <span class="token2">// One color for each vertex. They were generated randomly.</span> static const GLfloat g_color_buffer_data<span class="token1">[</span><span class="token1">]</span> <span class="token">=</span> <span class="token1">{</span> <span class="token6">0.583</span>f<span class="token1">,</span> <span class="token6">0.771</span>f<span class="token1">,</span> <span class="token6">0.014</span>f<span class="token1">,</span> <span class="token6">0.609</span>f<span class="token1">,</span> <span class="token6">0.115</span>f<span class="token1">,</span> <span class="token6">0.436</span>f<span class="token1">,</span> <span class="token6">0.327</span>f<span class="token1">,</span> <span class="token6">0.483</span>f<span class="token1">,</span> <span class="token6">0.844</span>f<span class="token1">,</span> <span class="token6">0.822</span>f<span class="token1">,</span> <span class="token6">0.569</span>f<span class="token1">,</span> <span class="token6">0.201</span>f<span class="token1">,</span> <span class="token6">0.435</span>f<span class="token1">,</span> <span class="token6">0.602</span>f<span class="token1">,</span> <span class="token6">0.223</span>f<span class="token1">,</span> <span class="token6">0.310</span>f<span class="token1">,</span> <span class="token6">0.747</span>f<span class="token1">,</span> <span class="token6">0.185</span>f<span class="token1">,</span> <span class="token6">0.597</span>f<span class="token1">,</span> <span class="token6">0.770</span>f<span class="token1">,</span> <span class="token6">0.761</span>f<span class="token1">,</span> <span class="token6">0.559</span>f<span class="token1">,</span> <span class="token6">0.436</span>f<span class="token1">,</span> <span class="token6">0.730</span>f<span class="token1">,</span> <span class="token6">0.359</span>f<span class="token1">,</span> <span class="token6">0.583</span>f<span class="token1">,</span> <span class="token6">0.152</span>f<span class="token1">,</span> <span class="token6">0.483</span>f<span class="token1">,</span> <span class="token6">0.596</span>f<span class="token1">,</span> <span class="token6">0.789</span>f<span class="token1">,</span> <span class="token6">0.559</span>f<span class="token1">,</span> <span class="token6">0.861</span>f<span class="token1">,</span> <span class="token6">0.639</span>f<span class="token1">,</span> <span class="token6">0.195</span>f<span class="token1">,</span> <span class="token6">0.548</span>f<span class="token1">,</span> <span class="token6">0.859</span>f<span class="token1">,</span> <span class="token6">0.014</span>f<span class="token1">,</span> <span class="token6">0.184</span>f<span class="token1">,</span> <span class="token6">0.576</span>f<span class="token1">,</span> <span class="token6">0.771</span>f<span class="token1">,</span> <span class="token6">0.328</span>f<span class="token1">,</span> <span class="token6">0.970</span>f<span class="token1">,</span> <span class="token6">0.406</span>f<span class="token1">,</span> <span class="token6">0.615</span>f<span class="token1">,</span> <span class="token6">0.116</span>f<span class="token1">,</span> <span class="token6">0.676</span>f<span class="token1">,</span> <span class="token6">0.977</span>f<span class="token1">,</span> <span class="token6">0.133</span>f<span class="token1">,</span> <span class="token6">0.971</span>f<span class="token1">,</span> <span class="token6">0.572</span>f<span class="token1">,</span> <span class="token6">0.833</span>f<span class="token1">,</span> <span class="token6">0.140</span>f<span class="token1">,</span> <span class="token6">0.616</span>f<span class="token1">,</span> <span class="token6">0.489</span>f<span class="token1">,</span> <span class="token6">0.997</span>f<span class="token1">,</span> <span class="token6">0.513</span>f<span class="token1">,</span> <span class="token6">0.064</span>f<span class="token1">,</span> <span class="token6">0.945</span>f<span class="token1">,</span> <span class="token6">0.719</span>f<span class="token1">,</span> <span class="token6">0.592</span>f<span class="token1">,</span> <span class="token6">0.543</span>f<span class="token1">,</span> <span class="token6">0.021</span>f<span class="token1">,</span> <span class="token6">0.978</span>f<span class="token1">,</span> <span class="token6">0.279</span>f<span class="token1">,</span> <span class="token6">0.317</span>f<span class="token1">,</span> <span class="token6">0.505</span>f<span class="token1">,</span> <span class="token6">0.167</span>f<span class="token1">,</span> <span class="token6">0.620</span>f<span class="token1">,</span> <span class="token6">0.077</span>f<span class="token1">,</span> <span class="token6">0.347</span>f<span class="token1">,</span> <span class="token6">0.857</span>f<span class="token1">,</span> <span class="token6">0.137</span>f<span class="token1">,</span> <span class="token6">0.055</span>f<span class="token1">,</span> <span class="token6">0.953</span>f<span class="token1">,</span> <span class="token6">0.042</span>f<span class="token1">,</span> <span class="token6">0.714</span>f<span class="token1">,</span> <span class="token6">0.505</span>f<span class="token1">,</span> <span class="token6">0.345</span>f<span class="token1">,</span> <span class="token6">0.783</span>f<span class="token1">,</span> <span class="token6">0.290</span>f<span class="token1">,</span> <span class="token6">0.734</span>f<span class="token1">,</span> <span class="token6">0.722</span>f<span class="token1">,</span> <span class="token6">0.645</span>f<span class="token1">,</span> <span class="token6">0.174</span>f<span class="token1">,</span> <span class="token6">0.302</span>f<span class="token1">,</span> <span class="token6">0.455</span>f<span class="token1">,</span> <span class="token6">0.848</span>f<span class="token1">,</span> <span class="token6">0.225</span>f<span class="token1">,</span> <span class="token6">0.587</span>f<span class="token1">,</span> <span class="token6">0.040</span>f<span class="token1">,</span> <span class="token6">0.517</span>f<span class="token1">,</span> <span class="token6">0.713</span>f<span class="token1">,</span> <span class="token6">0.338</span>f<span class="token1">,</span> <span class="token6">0.053</span>f<span class="token1">,</span> <span class="token6">0.959</span>f<span class="token1">,</span> <span class="token6">0.120</span>f<span class="token1">,</span> <span class="token6">0.393</span>f<span class="token1">,</span> <span class="token6">0.621</span>f<span class="token1">,</span> <span class="token6">0.362</span>f<span class="token1">,</span> <span class="token6">0.673</span>f<span class="token1">,</span> <span class="token6">0.211</span>f<span class="token1">,</span> <span class="token6">0.457</span>f<span class="token1">,</span> <span class="token6">0.820</span>f<span class="token1">,</span> <span class="token6">0.883</span>f<span class="token1">,</span> <span class="token6">0.371</span>f<span class="token1">,</span> <span class="token6">0.982</span>f<span class="token1">,</span> <span class="token6">0.099</span>f<span class="token1">,</span> <span class="token6">0.879</span>f <span class="token1">}</span><span class="token1">;</span> ``` ``` 缓冲区的创建、绑定和填充方法和之前一样: ``` <pre class="calibre16">``` GLuint colorbuffer<span class="token1">;</span> <span class="token3">glGenBuffers</span><span class="token1">(</span><span class="token6">1</span><span class="token1">,</span> <span class="token">&</span>colorbuffer<span class="token1">)</span><span class="token1">;</span> <span class="token3">glBindBuffer</span><span class="token1">(</span>GL_ARRAY_BUFFER<span class="token1">,</span> colorbuffer<span class="token1">)</span><span class="token1">;</span> <span class="token3">glBufferData</span><span class="token1">(</span>GL_ARRAY_BUFFER<span class="token1">,</span> <span class="token3">sizeof</span><span class="token1">(</span>g_color_buffer_data<span class="token1">)</span><span class="token1">,</span> g_color_buffer_data<span class="token1">,</span> GL_STATIC_DRAW<span class="token1">)</span><span class="token1">;</span> ``` ``` 配置也一样: ``` <pre class="calibre16">``` <span class="token2">// 2nd attribute buffer : colors</span> <span class="token3">glEnableVertexAttribArray</span><span class="token1">(</span><span class="token6">1</span><span class="token1">)</span><span class="token1">;</span> <span class="token3">glBindBuffer</span><span class="token1">(</span>GL_ARRAY_BUFFER<span class="token1">,</span> colorbuffer<span class="token1">)</span><span class="token1">;</span> <span class="token3">glVertexAttribPointer</span><span class="token1">(</span> <span class="token6">1</span><span class="token1">,</span> <span class="token2">// attribute. No particular reason for 1, but must match the layout in the shader.</span> <span class="token6">3</span><span class="token1">,</span> <span class="token2">// size</span> GL_FLOAT<span class="token1">,</span> <span class="token2">// type</span> GL_FALSE<span class="token1">,</span> <span class="token2">// normalized?</span> <span class="token6">0</span><span class="token1">,</span> <span class="token2">// stride</span> <span class="token1">(</span>void<span class="token">*</span><span class="token1">)</span><span class="token6">0</span> <span class="token2">// array buffer offset</span> <span class="token1">)</span><span class="token1">;</span> ``` ``` 现在,顶点着色器中,我们已能访问这个额外的缓冲区: ``` <pre class="calibre16">``` <span class="token2">// Notice that the "1" here equals the "1" in glVertexAttribPointer</span> <span class="token3">layout</span><span class="token1">(</span>location <span class="token">=</span> <span class="token6">1</span><span class="token1">)</span> <span class="token4">in</span> vec3 vertexColor<span class="token1">;</span> ``` ``` 本例将不会在顶点着色器里做花哨的玩意,只是简单地过渡到片断着色器: ``` <pre class="calibre16">``` <span class="token2">// Output data ; will be interpolated for each fragment.</span> out vec3 fragmentColor<span class="token1">;</span> void <span class="token3">main</span><span class="token1">(</span><span class="token1">)</span><span class="token1">{</span> <span class="token1">[</span><span class="token1">.</span><span class="token1">.</span><span class="token1">.</span><span class="token1">]</span> <span class="token2">// The color of each vertex will be interpolated</span> <span class="token2">// to produce the color of each fragment</span> fragmentColor <span class="token">=</span> vertexColor<span class="token1">;</span> <span class="token1">}</span> ``` ``` 片断着色器中,要再次声明片断颜色: ``` <pre class="calibre16">``` <span class="token2">// Interpolated values from the vertex shaders</span> <span class="token4">in</span> vec3 fragmentColor<span class="token1">;</span> ``` ``` …然后把它的值赋给输出颜色: ``` <pre class="calibre16">``` <span class="token2">// Output color = color specified in the vertex shader,</span> <span class="token2">// interpolated between all 3 surrounding vertices</span> color <span class="token">=</span> fragmentColor<span class="token1">;</span> ``` ``` 于是得到: ![](https://box.kancloud.cn/2015-11-02_5636f303ab2c9.png) 额,好丑。为了搞清楚,我们先看看各画一个看起来“远”和“近”的三角形,会发生什么: ![](https://box.kancloud.cn/2015-11-02_5636f303bd5dc.png) 似乎挺好。现在画“远”的三角形: ![](https://box.kancloud.cn/2015-11-02_5636f303ce1b6.png) 它遮住了“近”三角形!它本应该画在“近”三角形后面的!我们的立方体就有这个问题:一些理应被遮挡的面,因为绘制时间晚,实际可见。我们将用深度缓存(Z-Buffer)算法解决它。 *便签1*: 如果你没发现问题,把相机放到(4,3,-3)试试 *便签2*: 如果“类似于位置,颜色是一种属性”,那为什么颜色要声明 vec3 fragmentColor,而位置不需要?实际上,位置有点特殊:它是唯一必须赋初值的(否则OpenGL不知道在哪画三角形)。所以在顶点着色器里, gl\_Position是内置变量。 ## 深度缓存(Z-Buffer)The Z-Buffer 该问题的解决方案是:在缓冲区中存储每个片断的深度(即“Z”值);而每次画片断时,先确保当前片断确实比先前画的片断更近。 你可以自己实现,但让硬件自己去做更简单: ``` <pre class="calibre16">``` <span class="token2">// Enable depth test</span> <span class="token3">glEnable</span><span class="token1">(</span>GL_DEPTH_TEST<span class="token1">)</span><span class="token1">;</span> <span class="token2">// Accept fragment if it closer to the camera than the former one</span> <span class="token3">glDepthFunc</span><span class="token1">(</span>GL_LESS<span class="token1">)</span><span class="token1">;</span> ``` ``` 这就解决之前所有问题了。 ![](https://box.kancloud.cn/2015-11-02_5636f303dd9ff.png) ## 练习 - 在不同的位置画立方体和三角形。你需要生成两个MVP矩阵,在主循环中做两次绘制调用,但只需一个着色器。 - 自己生成颜色值。一些提示:随机生成,使每次运行颜色都不同;依据顶点的位置;将前二者结合;或其他的创新想法。若你不了解C,参考以下语法: ``` <pre class="calibre16">``` static GLfloat g_color_buffer_data<span class="token1">[</span><span class="token6">12</span><span class="token">*</span><span class="token6">3</span><span class="token">*</span><span class="token6">3</span><span class="token1">]</span><span class="token1">;</span> <span class="token4">for</span> <span class="token1">(</span>int v <span class="token">=</span> <span class="token6">0</span><span class="token1">;</span> v <span class="token"><</span> <span class="token6">12</span><span class="token">*</span><span class="token6">3</span> <span class="token1">;</span> v<span class="token">++</span><span class="token1">)</span><span class="token1">{</span> g_color_buffer_data<span class="token1">[</span><span class="token6">3</span><span class="token">*</span>v<span class="token">+</span><span class="token6">0</span><span class="token1">]</span> <span class="token">=</span> your red color here<span class="token1">;</span> g_color_buffer_data<span class="token1">[</span><span class="token6">3</span><span class="token">*</span>v<span class="token">+</span><span class="token6">1</span><span class="token1">]</span> <span class="token">=</span> your green color here<span class="token1">;</span> g_color_buffer_data<span class="token1">[</span><span class="token6">3</span><span class="token">*</span>v<span class="token">+</span><span class="token6">2</span><span class="token1">]</span> <span class="token">=</span> your blue color here<span class="token1">;</span> <span class="token1">}</span> ``` ``` - 完成上面习题后,试令颜色在每帧都改变。你需要在每一帧都调用glBufferData。请确保已先绑定(glBindBuffer)了合适的缓冲区!