今天我们将在屏幕上同时显示三角形和正方形,并且移动它们。移动一个物体,我们称之为转移.

 


OpenGL ES有三种不同类型的变换,它们是:

 

  1. 转移(Translate)- 在3D空间中移动物体

  2. 旋转(Rotate) – 绕X, Y, 或者 Z 轴选择

  3. 缩放(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

 

此变量保存当前旋转的角度。

 

现在,切换到drawViewglLoadIdentity()函数调用之前加入下列代码:

1
rota += 0.5

 

我们做的是每次绘制两个物体时将旋转角度增加0.5º。最后,修改glRotatef()函数调用:

1
glRotatef(rota, 0.0, 0.0,1.0);

 

我们在每次绘制物体时增加旋转角度导致物体旋转。第一次绘制物体时,它们被旋转0.5º;第二次绘制时,被旋转1.0º等。

 

按“Build and Run”,此两个物体看上去像从侧面观察时旋转的轮胎。

 

试验

 

在完成此教程前,我还要做些事情:

 

  1. 1. 改变旋转的轴。使Z轴为0.0依次使物体绕X轴和Y轴旋转,你一定会明白绕不同轴时物体是怎样旋转的。

  2. 2. 将各当前旋转轴值1.0修改为-1.0。注意到此时物体将按相反的方向旋转。

  3. 3. 修改glRotatef()的rota参数为-rota。发生了什么?

 

希望你能从此教程中学到了一点东西。这里是完整的代码:

 

AppleCoder-OpenGLES-03.zip

 

我不知道你们感觉怎样,但我已经对白色的物体感到厌倦了。下一次,我们将为这些物体增加颜色。

 

原文见:OpenGL ES 03 – Transformations 作者:Simon Maurice