# 第四课:彩色立方体
# 第四课:彩色立方体
欢迎来到第四课!你将学到:
- 画立方体,代替单调的三角形
- 加上绚丽的色彩
- 学习深度缓存(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)了合适的缓冲区!