严格地说,正方形不是OpenGL ES的基元,但是它们确实是很基本的,而且渲染正方形像渲染三角形一样容易。本教程中,我们将把绘制三角形基元的代码转变成正方形的代码。我们仍将采用静态渲染,但我们会很简单地介绍一下变换(如移动)。当然,一旦我们绘制了正方形,我们就可以绘制正方体,然后甚至是带纹理映射的正方体……
简单概括一下本教程
上一次教程,我们在一个“空白画布”Xcode项目上渲染了一个实体三角形。然后建立一个顶点数组,通过使用glVertexPointer()告诉OpenGL数据及其格式,再使用glDrawArrays()渲染出来。
今天,我们将使用上次的代码,但把三角形变为正方形。我们仅需要改动其中几行代码。第一部分应该很明显,我们应为正方形指定4点而不是三角形的3点。然后我们通过传递给glDrawArrays()不同的参数告诉OpenGL以不同的方式绘画。
我们开始吧。
定义正方形顶点
打开上次教程的Xcode项目并找到rawView 方法。为riangleVertices[] 常量加上注释-先不要删除,因为当我们讨论变换时会需要用到它们-加入下列代码:
1 2 3 4 5 6 7 8 | const GLfloat squareVertices[] = { -1.0, 1.0, -6.0, // Top left -1.0, -1.0, -6.0, // Bottom left 1.0, -1.0, -6.0, // Bottom right 1.0, 1.0, -6.0 // Top right }; |
这定义了我们的正方形。注意反时针方向的正方形顶点(4个点)。
然后我们在主体部分为绘制三角形代码加上注释,稍后我们也会用回这些代码的。为三个函数glVertexArray(), glEnableClientState()和glDrawArrays()加上注释并加入下列代码:
1 2 3 4 5 | glVertexPointer(3, GL_FLOAT, 0, squareVertices); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
这三个函数只有少许不同。
1 | glVertexPointer(3, GL_FLOAT, 0, squareVertices); |
这里唯一的变化是使用了不同的顶点数组,即正方形数组而不是三角形数组。
glEnableClientState()仍然是通知OpenGL从顶点数组绘制(而不是绘制颜色数组或其它)。
1 | glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
最主要的变化就在这里了。上次的教程我们使用GL_TRIANGLES作为第一个参数,3为第三个参数。记住第二个参数代表数组开始位置的偏移量,因为我们的顶点数组只包括了正方形,所以它还是0。
第一参数是OpenGL的绘图模式,你已经见过其中两种。就此机会我讨论一下各种不同的绘图模式。它们是:
GL_POINTS
GL_LINES
GL_LINE_LOOP
GL_LINE_STRIP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
我们还没有讨论过点和线,所以我们先把重点放在后面三种与三角形有关的模式。在我开始讲解之前,我要提醒你们一个顶点数组可能包括不止一个三角形,所以虽然你只见过一个顶点数组包括一个对象,但并不限如此。
GL_TRIANGLES – 表示OpenGL将以是三个顶点一组来处理顶点数组。所以,第一组三个顶点代表三角形顶点1,2和3。然后再处理接下来的一组三个顶点,直到数组的尾部。
GL_TRIANGLE_STRIP – OpenGL将从前两个顶点开始,对于各个后续顶点,它将与前两个顶点一起构成三角形。也就是说,squareVertices[6~8]是与squareVerticies[0~2]和squareVerticies[3~5]一起构成三角形的。而squareVertices[9~11]是与squareVertices[3~5]和squareVertices[6~8]构成三角形的。如此这般,完成整个数组。
注意:squareVertices[0~2]表示:
squareVertices[0] – X 坐标
squareVertices[1] – Y 坐标
squareVertices[2] – Z 坐标
如果你还不明白,我将在随后的示例中进一步说明。
GL_TRIANGLE_FAN – 对于最前面两个顶点之后的每个后续顶点,OpenGL将使用前一个顶点以及第一个顶点构成三角形。所以,对于squareVertices[6~8],它是通过与squareVertices[3~5] (前一个顶点)和squareVertices[0~2] (第一个顶点)一起构成三角形的。
我们使用的是GL_TRIANGLES_FAN。按下“Build & Go”后,你可以在屏幕上看到如下白色正方形:

回头看看顶点数组。想象一下三角形是这样被绘制从而构成正方形的。OpenGL是这样进行渲染的。
三角形顶点1: squareVertices[0~2] — 正方形的左上角
三角形顶点2: squareVertices[3~5] — 正方形的左下角
三角形顶点3: squareVertices[6~8] — 正方形的右下角
OpenGL使用上叙三点绘制的三角形组成了正方形的左下部分。设想下按对角线从左上角开始到右下角将正方形分成两部分。注意两个三角形是怎样构成的?OpenGL只是画了正方形左下方一半。
注意:squareVertices[0~2]表示:
squareVertices[0] - X坐标
squareVertices[1] – Y坐标
squareVertices[2] – Z坐标
三角形顶点1: squareVertices[9~11] – 正方形右上方顶点
三角形顶点2: squareVertices[6~8] — 前一个顶点,右下顶点
三角形顶点3: squareVertices[0~2] — 第一顶点,左上顶点
只要使用一个新点,OpenGL就能渲染三角形以完成整个正方形的渲染。
GL_TRIANGLE_STRIP
回到代码将glDrawArrays()第一个参数从GL_TRIANGLE_FAN改为GL_TRIANGLE_STRIP:
1 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
按 “Build & Go” ,你将看到下列图形:

让我们分析下为什么在绘制方法中的一个变化就会导致我们得不到正方形的结果。OpenGL以以下方法处理顶点数组:
三角形顶点 1: squareVertices[0~2] — 左上
三角形顶点 2: squareVertices[3~5] — 左下
三角形顶点 3: squareVertices[6~8] — 右下
OpenGL现在是使用最前面的三点渲染三角形,因此,正方形左下半部分三角形的渲染已前面例子的结果一样。
三角形顶点 1: squareVertices[9~11] – 右上
三角形顶点 2: squareVertices[6~8] — 前一个顶点,右下
三角形顶点 3: squareVertices[3~5] — 再前一个顶点,左下
OpenGL现在是使用这三点来渲染三角形。在本例中,使渲染的三角形与构成正方形所需的三角形相比,正好翻转了90度。
如果我们用不同方式提供顶点数组,我们有可能像使用GL_TRIANGLE_STRIP获得正确正方形,就像使用GL_TRIANGLE_FAN一样。需要记住绘制方法必须与顶点数组一起考虑,否则有可能得到奇怪的结果,就像我们将FAN改为STRIP时一样。
注意:如果你以不同方式设定顶点数组,使用GL_TRIANGLE_STRIP你仍可以画出正确的正方形。例如:
1 2 3 4 5 6 7 | const GLfloat stripSquare[] = { -1.0, -1.0, -6.0, // 左下 1.0, -1.0, -6.0, // 右下 -1.0, 1.0, -6.0, // 左上 1.0, 1.0, -6.0 // 右上 }; |
使用上述三点,可以看到第一个三角形是通过前三个顶点构成的,获得的三角形如下:

现在,通过指定右上顶点(P4),与左上(P3)和右下的再前一个顶点(P2)可以构成一个新三角形。新顶点用橘红,绿色和红色表示如下:

我们正确地绘制了一个正方形。最终的结果是一样的,但需要提醒你的是,指定的顶点数组必须与绘图方法匹配。
结论
我们已经讨论了三角形和正方形。还剩下点和线部分。这两种基元都十分简单,将在下一篇教程中涉及。对于本次教程已经涉及的部分,下次我们将进行混色处理。
在完成了物体着色后,我们将移动和进行3D纹理映射。当然,它不会是Doom 3式的程序,不过我们可以由此开始建立3D物体。然后,我们就将进入3D世界了。
本教程的源代码:AppleCoder-OpenGLES-02.zip



