今天我们将在屏幕上同时显示三角形和正方形,并且移动它们。移动一个物体,我们称之为转移.
OpenGL ES有三种不同类型的变换,它们是:
-
• 转移(Translate)- 在3D空间中移动物体
-
• 旋转(Rotate) – 绕X, Y, 或者 Z 轴选择
-
• 缩放(Scale) – 改变物体的大小。这主要用于2D正交投影系统,因为在3D空间,一个物体移动得越远(即Z坐标的负值越大),物体就会显示得更小。但是当然可以用来产生“特殊效果”。
我展示这几种不同的功能,我们将使用转移功能将正方形和三角形同时移动到屏幕上,然后运用另外两种变换功能。
转移
为实现转移,OpenGL ES提供了一个函数glTranslatef()。注意到函数中的“f”了吗? 那表示我们将为OpenGL提供浮点数据。OpenGL ES还提供了通过glTranslatex()函数使用定点数据的可能。定点数据通常用于没有专用浮点运算处理器的硬件上,但iPhone具有浮点运算处理器,所以我们并不需要使用定点运算。
我只是提醒大家当你的Xcode代码完成提示使用glTranslatex()时,你能够知道两者的区别。
好了,到了加入一些代码的时候了。运行Xcode打开你的项目。我希望你已听从了我的建议,只是将三角形渲染部分加了注释而没有删除,否则的话,你就需要重新输入这些代码。
首先,看看两个顶点数组。我们要对定点数据做些修改,但仅限于Z坐标。将所有Z坐标值改为0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | const GLfloat triangleVertices[] = { 0.0,1.0,0.0, // Triangle top centre -1.0, -1.0, 0.0, // bottom left 1.0, -1.0, 0.0 // bottom right }; const GLfloat squareVertices[] = { -1.0, 1.0, 0.0, // Top left -1.0, -1.0, 0.0, // Bottom left 1.0, -1.0, 0.0, // Bottom right 1.0, 1.0, 0.0 // Top right }; |
你还记得为什么我们要将Z坐标设为-6.0? 那是由于我们的“摄像头”处于 (0.0, 0.0, 0.0)所以我们要将对象设置在屏幕后。我们要做的是使用glTranslatef()函数将它们向后移动6点而不是在定点数组中设置。
首先,我们需要通知OpenGL将对什么进行转移:是投影(视口)或对象(模型)。本文中,是一个正方形和一个三角形。在drawView方法中glClear()之后调用下列OpenGL函数:
1 2 3 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); |
glMatrixMode是通知OpenGL对顶点数据而不是投影进行处理。在setupView方法中,我们调用了同样的函数,但是是使用了GL_PROJECTION参数。OpenGL引擎是一个“状态”机器,一旦设定了“状态”,它将一直保持到你通知其改变。所以,矩阵模式一直保持为GL_PROJECTION直到调用glMatrixMode(GL_MODELVIEW)进入模型模式。在设定OpenGL状态为 GL_MODELVIEW后,它将保持到我们通知其改变。
实际上,如果需要最佳性能,我们可以在第一篇教程示例的setupView方法最后进行此调用。但由于这只是一个教程,所以我们仍然把它放在drawView方法中。
我还没有讨论过OpenGL ES的投影模式,如果你还不完全明白上述方法也不必担心。我们所作的只是将一些对象置于屏幕之上并在学习OpenGL ES的过程中进行一些测试。
现在,移除绘制三角形代码的注释:
1 2 3 4 5 | glVertexPointer(3,GL_FLOAT,0, triangleVertices); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLES, 0,3); |
在这三行代码前加入下面两行:
1 2 3 4 5 6 7 8 9 | glLoadIdentity(); glTranslatef(-1.5, 0.0, -6.0); glVertexPointer(3,GL_FLOAT, 0, triangleVertices); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLES, 0, 3); |
glLoadIdentity() 是一个方便的方法将一切复位至初始状态。如果我们没有调用此函数,glTranslatef()将继续移动对象至屏幕的左后方直至其消失。实际上,我以后(下下篇教程)会讨论一种更好的方法,所以现在暂时接受一下将对象数据复位。
glTranslatef()接受3个参数:
1 | glTranslatef(GLfloat xtrans, GLfloat ytrans, GLfloat Ztrans); |
继续前,请复习一下3D空间中图形的绘制。

记住“摄像机”是处于(0.0, 0.0, 0.0)。上文中glTranslatef() 的调用使用以下数值:
1 2 3 4 5 | xtrans = -1.5 ytrans = 0.0 ztrans = -6.0 |
下面一件你要记得的事情是当我们绘制正方形和三角形,它们需要出现在屏幕的中心。如果我们使用上一教程的代码绘制,它们将互相重叠。
所以,我将三角形中心的X坐标向左移动1.5点。参照上面坐标空间的图像,可以知道屏幕中心的左方是负数,所以为-1.5。
然后将Z变换值改为-6.0。
这样三角形向左移动1.5点及向后移动6点。
移动正方形
正方形移动的代码几乎与三角形的一样。如下:
1 2 3 4 5 6 7 8 9 | glLoadIdentity(); glTranslatef(1.5, 0.0, -6.0); glVertexPointer(3, GL_FLOAT, 0, squareVertices); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
这一次,我们通过向glTranslatef()中的xtrans传递正值向右移动正方形。
按“Build and Go”,屏幕上将出现下面图形:

注意到Y坐标仍然处于屏幕中心,物体大小不变。因为我们将其向后移动6.0点,它们被并排显示出来。
继续前的试验
通过改变glTranslatef()中xtrans, ytrans, 以及ztrans的值进行试验。我花了近一个小时改变这些值来观察发生了什么。你甚至可以取消glLoadIdentity()的注释来进行观察。
旋转
将代码更改为试验前的样子。由于我们的物体是2D物体(处于3D空间),我们在2D空间旋转它们。稍后,我们将创建3D物体,并在3D中旋转(是的,我们还以对其进行纹理映射…)
旋转很简单:
1 | glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); |
此函数很容易使用。首先,需要指定旋转的角度,然后指定旋转的轴。
我将以两种方式展示旋转。首先,我们进行静态旋转,然后保持旋转使其动画化。
首先,我们进行简单旋转。在drawView方法中修改代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | glLoadIdentity(); glTranslatef(-1.5, 0.0, -6.0); glRotatef(45.0, 0.0, 0.0, 1.0); // Add this line glVertexPointer(3, GL_FLOAT, 0, triangleVertices); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLES, 0, 3); glLoadIdentity(); glTranslatef(1.5, 0.0, -6.0); glRotatef(45.0, 0.0, 0.0, 1.0); // Add this line glVertexPointer(3, GL_FLOAT, 0, squareVertices); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
上面做的是将三角形和正方形绕Z轴旋转45º。看到Z参数值1.0了吗?它是告诉OpenGL绕Z轴旋转45º。
修改代码并按“Build and Run”,你将见到下面画面:

绕Z轴转动使物体在屏幕上“旋转” – 就像你在车旁观看轮胎旋转。记住Z轴是深入屏幕的。
绕X轴转动就像面对汽车观看轮胎旋转。绕Y轴旋转就像司机转动方向盘避免撞到你时的轮胎。如果你仍然感到困惑,不要紧,下一个例子我们将允许你试验一下旋转,你就会真正明白发生了什么。
物体旋转
为了使我们的正方形和三角形开始旋转,我们需要在每一帧增加角度。找到EAGLView.h增加下列变量:
1 | GLfloat rota; |
然后转到EAGLView.m在initWithCoder方法中animationInterval之后加入下列赋值语句:
1 | rota = 0.0 |
此变量保存当前旋转的角度。
现在,切换到drawView在glLoadIdentity()函数调用之前加入下列代码:
1 | rota += 0.5 |
我们做的是每次绘制两个物体时将旋转角度增加0.5º。最后,修改glRotatef()函数调用:
1 | glRotatef(rota, 0.0, 0.0,1.0); |
我们在每次绘制物体时增加旋转角度导致物体旋转。第一次绘制物体时,它们被旋转0.5º;第二次绘制时,被旋转1.0º等。
按“Build and Run”,此两个物体看上去像从侧面观察时旋转的轮胎。
试验
在完成此教程前,我还要做些事情:
-
1. 改变旋转的轴。使Z轴为0.0依次使物体绕X轴和Y轴旋转,你一定会明白绕不同轴时物体是怎样旋转的。
-
2. 将各当前旋转轴值1.0修改为-1.0。注意到此时物体将按相反的方向旋转。
-
3. 修改glRotatef()的rota参数为-rota。发生了什么?
希望你能从此教程中学到了一点东西。这里是完整的代码:
我不知道你们感觉怎样,但我已经对白色的物体感到厌倦了。下一次,我们将为这些物体增加颜色。



