基元是构成复杂物体的基本绘图元素。OpenGL ES 中可以使用的基元有点,线和三角形。我想这些是不需要解释的了。
首先,我们先看看一些代码,然后讨论一下这些代码的作用,你可以利用这些代码编写你自己的代码。
基元 #1 – 三角形
三角形是最“复杂”的基元,但它是如此简单和实用。这是你将要绘制的第一个OpenGL基元。在绘制三角形时,我们所需要做的只是提供给OpenGL三角形3个3D空间中的坐标,OpenGL就会将其渲染出来。
开始,先复制OpenGL ES 00教程的项目文件,或下载:AppleCoder-OpenGLES-00.tar.gz 。
在Xcode中打开项目,找到EAGLView.m 中drawView 方法。
首先需要定义三角形。我们必须理解两种类型的坐标:模型坐标(Model)和世界坐标(World)。模型坐标是以我们正在绘制的基元为参照的,而世界坐标则告诉OpenGL它与观察者的关系(观察者总是处于世界坐标的(0.0,0.0,0.0)。
第一个例子说明了这点。首先,用3个3D坐标(X,Y,Z)定义处于模型空间中的三角形:
1 2 3 4 5 6 7 8 9 | const GLfloat triangleVertices[] = { 0.0, 1.0, -6.0,// Triangle top centre -1.0, -1.0, -6.0,// bottom left 1.0, -1.0, -6.0,// bottom right }; |
这三个坐标定义了三角形,要注意它们是以反时针方向顺序定义的。尽管你可以顺时针或反时针方向定义,但一定要按顺序(译者注:废话,三角形再怎么定义,顺序也不会乱的)。我建议从反时针坐标开始,因为后面一些更高级的功能需要这样。
由于此教程是完全针对iPhone OpenGL ES初学者的,我在这里简单描述一下3D坐标系统。请看下图:

上图代表模型空间坐标或世界坐标。想象一下这就是你的电脑屏幕,X和Y分别指向水平和垂直方向,而Z则执行深度方向。原点在(0.0,0.0,0.0)。
让我们看一下前面描述的三角形顶点情况,第一点(0.0, 1.0, -6.0) 处于Y轴正中心(译者注:应该是X轴)向上一个单位并向屏幕后延伸6个单位。第二点处于Y轴左方1.0个单位,X轴下方(所以Y为-1),仍向屏幕里6.0个单位。第三点也可以此类推。
我将物体放在屏幕后(Z为负值)的原因是观察者总是处于(0.0,0.0,0.0),如果放置在屏幕前,OpenGL的深度测试将会失败不会将其渲染出来。
可能会有人反驳:“你指的是模型坐标而不是世界坐标。” 是的,说得很对,但当我们渲染此三角形时,OpenGL只是简单地将目标设置在(0.0,0.0,0.0)。所以我们将其置于屏幕后可以使其可见。当我们讨论各种变换(移动,旋转等)时,你们就可以看到有一些方法不需要将目标设在屏幕后(Z负值)就可使其可见。我们现在还是将z坐标设为-6.0。
绘图代码
现在,我们已介绍完了三角形坐标。下面我们要告诉OpenGL数据存在什么地方以及怎样绘制。这只需几行代码。找到drawView方法,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | - (void)drawView { const GLfloat triangleVertices[] = { 0.0, 1.0, -6.0, // Triangle top centre -1.0, -1.0, -6.0, // bottom left 1.0, -1.0, -6.0 // bottom right }; [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); // -- BEGIN NEW CODE glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glVertexPointer(3, GL_FLOAT, 0, triangleVertices); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLES, 0, 3); // -- END NEW CODE glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; [self checkGLError:NO]; } |
如你所见,只用四行代码就可实现三角形的渲染。我逐行解释,你们就知道这是多么的容易。
1 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
这一行是完成清屏。我们填入的控制位是告诉OpenGL使用我们在上次教程setupView方法中设定的颜色(黑色)清屏并清除深度缓存。注意,如果我们打开了深度缓存而且没有清除,那么渲染将不会进行。如果我们没有启用深度缓存,就不需要将GL_DEPTH_BUFFER_BIT传递到glClear()中。
好,我们已清除原先绘制在此缓存中的数据(记住,这是双缓冲动画;当绘制一个缓存时,另一个缓存正在被显示出来。)
1 | glVertexPointer(3, GL_FLOAT, 0, triangleVertices); |
此行代码是告诉OpenGL数据在什么地方以及采用什么格式。此函数有四个参数,简单解释如下:
-
尺寸 – 指每个坐标的数目值。我们使用3,代表(X,Y,Z)。如果你进行2D绘图,没有深度(Z值),那么此参数为2。
-
数据类型 – GL_FLOAT表示我们传递的是浮点数值。你还可以将其设为整数型。不过最好适应浮点数,因为3D世界是用浮点表示的。
-
跨度 – 此参数告诉OpenGL忽略各坐标间一定数量的字节。我们将其设为0。唯一需要用到此参数的情况是当你从文件中加载顶点数据,此文件具有额外的填充数据或颜色数据,比如Blender的文件。
-
数据指针 – 数据本身。
好,我们已经通知OpenGL清除缓存,指定了绘制物体的数据存在什么地方以及数据格式,现在我们要让OpenGL做些重要的事情了:
1 | glEnableClientState(GL_VERTEX_ARRAY); |
OpenGL是一个“状态”机器。这意味着你必须通过调用启动和禁止命令来打开或关闭一些功能。前面,我们已经使用过glEnable(),它影响OpenGL的“服务器”方。而glEnableClientState()将影响我们的程序方(即客户方)。我们所做的是通知OpenGL顶点数据处于一个顶点数组中并且打开OpenGL顶点绘制功能。有时一个顶点可以是一个颜色数组,在这种情况下我们应调用glEnableClientState(GL_COLOR_ARRAY);或者在纹理映射时也可能是一个纹理坐标数组 (别浪费口水!在涉及纹理映射前我们还是先完成基本图形绘制吧!!)
随着我们深入了解OpenGL,我们将使用不同的客户方状态并对此有更深的理解。
现在到了让OpenGL渲染三角形的命令了:
1 | glDrawArrays(GL_TRIANGLES, 0, 3); |
一旦此函数被调用,OpenGL将使用前面几个函数的信息来执行命令。一个填充了白色(白色是缺省绘图色)的三角形将会出现在屏幕上。三角形是填充了的物体,如果你想要绘制未填充的三角形,那么你需要使用不同的绘图方法。
分解此函数, 有三个参数:
-
绘图方式 – 我们传递了GL_TRIANGLES,这显而易见是要绘制一个三角形。然而当我们需要绘制正方形时,我们才会看到第一个参数的真正功力。
-
第一顶点 – 我们的数组包括了3个点,所以我们要OpenGL从数组中的第一个坐标(就像访问标准数组一样指定为0)画起,。如果在顶点数组中有多个基元,我们可以在这里设置一个偏移量。当我在后面的教程中绘制介绍更复杂的物体时,我会介绍怎样使用偏移量。现在我们将其设为0。
-
顶点数 – 它告诉OpenGL我们的数组中有多少个顶点需要绘制。由于我们绘制的是三角形,所以只有3点。正方形有四点,线有两点(或多点)而点只有一个顶点或多个(在渲染多点的情况下)。(译者注:真罗嗦!差点晕倒。)
在drawView方法中输入上述代码后,按“Build and Go”在仿真器上运行。你将看到:

一个白色的三角形显现在屏幕的中央。
在我们开始绘制其他基元前,试一下改变Z值。你就会明白将其改为0.0时会发生什么。没有任何东西被渲染出来。
有很多行代码需要敲入,但如果你就此明白OpenGL ES是怎样工作的,那我认为这一切是值得的。如果你以前试过”标准“OpenGL教程,我希望你可以看出OpenGL和OpenGL ES的区别来。
前瞻
下一篇教程将讲述正方形的绘制。源代码。AppleCoder-OpenGLES-01.zip



