学习内容
OpenGL ES的基本概念
Android下3D开发的基本知识
利用OpenGL ES进行2D图形的开发
利用OpenGL ES进行3D图形的开发
能力目标
了解OpenGL ES的基本概念
了解Android下3D开发的基本知识
掌握如何利用OpenGL ES进行2D图形的开发
掌握如何利用OpenGL ES进行3D图形的开发
本章简介
游戏在Android中一个非常重要的开发方向,也是可以预料到的未来Android开发中最赚钱的。从最初单机2D游戏到现在的网络3D游戏,无论是显示效果,还是娱乐性上都有了显著的提高。这其中最重要的功臣就是扮演着重要角色的3D图形库。目前在PC领域,一直有两种标准的3D API进行在竞争:OpenGL 和 DirectX。一般主流的游戏和显卡都支持这两种渲染方式,DirectX在Windows平台上有很大的优势,但是 OpenGL 具有更好的跨平台性。在移动平台上使用到的最多的3D图形库就是本章中将要讲到的OpenGL ES,我们Android系统的3D 引擎采用的是OpenGL ES图形库。
核心技能部分
3.1 3D开发基本知识
OpenGL(Open Graphics Library)是个定义了一个跨编程语言、跨平台的编程接口,它用于二维或三维图象绘制。OpenGL是个专业的图形程序接口,是一个功能强大,调用方便的底层图形库。
OpenGL ES(Open Graphics Library for Embedded System)是一套为手持和嵌入式系统设计的2D/3D轻量图形库,它是基于OpenGL API设计的,是OpenGL三维图形API的一个子集。OpenGL ES是从OpenGL裁剪定制而来的,它去除了OpenGL中很多特性,并针对移动设备改善了图形显示效果,大大降低了内存消耗。
推广维护,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。OpenGL ES的官方主页是http:www.khronos.org/opengles
Android系统使用 OpenGL ES的标准接口来支持3D图形功能,android 3D 图形系统也分为 java 框架和本地代码两部分。本地代码主要是实现的 OpenGL 接口的库,在 Java 框架层,javax.microedition.khronos.opengles 是 java 标准的 OpenGL 包,android.opengl包提供了 OpenGL 系统和 Android GUI 系统之间的联系。
OpenGL ES不仅可以绘制3D图形,还可以绘制2D图形。OpenGL ES只能绘制三角形,但这并不影响多边形的绘制,因为任何模型形都可以拆分成三角形。
OpenGL ES的坐标系是三维的,其中坐标系原点是手机的中心。如下图3.1.1所示:
图3.1.1 OpenGL ES坐标系
3.2 绘制2D图形
在3D桌球之类的游戏中,华丽的界面、流畅的体验一定给大家留下了深刻的印象。相信大家一定会希望自己也能开发出属于自己的3D游戏。其实所谓的3D其实也比较简单,它是由大量的平面图形按一定的方式组合而成的,比如如下图3.1.2中的桌球游戏的界面,就可以简单的认为是由一些矩形、球形组合而成。在接下来的两节中,我们就分别从2D及3D的角度来讲解Android中如何利用OpenGL ES绘制图形。
图3.1.2 3D桌球
像第一章Android中的绘图一样,为了方便程序的编写,我们也先搭建一个程序框架,以后的程序就在这个框架的基础之上进行编写。
搭建OpenGL ES开发框架,需要以下两步:
(1)提供一个实现了Renderer接口的回调类,并实现这个接口的相关方法。
(2)在Activity类中显示第一步中的Renderer。
示例2.1
搭建OpenGL ES的基本开发框架。
首先定义Renderer接口的实现类,代码如下:
public class MyRenderer implements Renderer {
@Override
public void onDrawFrame(GL10 gl) {
//可以在这个方法中绘制2D或3D图形
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//可以在这个方法中设置场景的大小
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//可以在这个方法中做一些初始化工作,比如设置背景颜色、启动平滑模型等
}
}
上述代码中的onSurfaceCreated()方法在创建或重建OpenGL ES绘制窗口时会被调用到。可以在这个方法中做一些初始化的操作,比如设置背景颜色、启动平滑模型等。onSurfaceChanged()在窗口尺寸发生变化时被调用,不管窗口的大小是否发生改变,该方法在程序启动时至少执行一次,可以在这个方法中设置场景的大小。onDrawFrame( )在绘制每一帧时被调用,类似于View中的onDraw()方法,一般在这个方法中绘制2D或3D图形。注意,在绘图之前一般需要将屏幕清除成指定的颜色、清除深度缓存并重置场景。
上面3个方法的第一个参数的类型都是GL1.0,这是OpenGL ES 1.0的接口,我们就是利用它业完成2D/3D图形的绘制及渲染的。
再提供一个Activity类,用来显示我们在上面Renderer类中绘制的图形,具体的代码如下:
public class Frame3DActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView glView = new GLSurfaceView(this);
MyRenderer renderer = new MyRenderer();
glView.setRenderer(renderer);
setContentView(glView);
}
}
接下来,我们在这个程序框架上绘制2D图形。
示例2.2:
在手机屏幕上绘制一个三角形和一个四边形,要求三角形沿X轴旋转、四边开沿Y轴旋转。
我们只需要修改示例2.1中的MyRenderer类,为其增加图形绘制的功能,具体代码如下:
public class MyRenderer implements Renderer {
int one = 0x10000;
private float triRotate;
private float quaterRotate;
// 三角形三个顶点:上顶点、左下点、右下点
private IntBuffer triBuffer = IntBuffer.wrap(new int[] { 0, one, 0, -one, -one, 0, one, -one, 0, });
private IntBuffer quaterBuffer = IntBuffer.wrap(new int[] { one, one, 0, -one, one, 0, one, -one, 0, -one, -one, 0 });
// 三角形的顶点颜色值(r,g,b,a)
private IntBuffer colorBuffer = IntBuffer.wrap(new int[] { one, 0, 0, one, 0, one, 0, one, 0, 0, one, one});
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度缓存
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// 允许设置顶点
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//设置颜色数组,开启颜色渲染功能
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);
gl.glLoadIdentity();// 重置当前的模型观察矩阵
gl.glTranslatef(-1.5f, 0.0f, -6.0f);// 左移 1.5 单位,并移入屏幕 6.0
gl.glRotatef(triRotate, 0.0f, 1.0f, 0.0f); //设置旋转,应该在坐标确定后再旋转
gl.glVertexPointer(3, GL10.GL_FIXED, 0, triBuffer);// 设置三角形
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);// 绘制三角形
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
gl.glColor4f(1.0f, 0.0f, 0.5f, 1.0f);
gl.glLoadIdentity();// 重置当前的模型观察矩阵
gl.glTranslatef(1.5f, 0.0f, -6.0f);
gl.glRotatef(quaterRotate, 1.0f, 0.0f, 0.0f); //设置旋转
gl.glVertexPointer(3, GL10.GL_FIXED, 0, quaterBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);// 取消顶点设置
//改变旋转角度
triRotate += 0.5f;
quaterRotate -= 0.5f;
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
float ratio = (float) width / height;
gl.glViewport(0, 0, width, height);// 设置OpenGL场景的大小
gl.glMatrixMode(GL10.GL_PROJECTION);// 设置投影矩阵
gl.glLoadIdentity();// 重置投影矩阵
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);// 设置视口的大小
gl.glMatrixMode(GL10.GL_MODELVIEW);// 选择模型观察矩阵
gl.glLoadIdentity();// 重置模型观察矩阵
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glShadeModel(GL10.GL_SMOOTH);// 启用阴影平滑
gl.glClearColor(0, 0.5f, 0.5f, 0.5f);// 黑色背景
gl.glClearDepthf(1.0f);// 设置深度缓存
gl.glEnable(GL10.GL_DEPTH_TEST);// 启用深度测试
gl.glDepthFunc(GL10.GL_LEQUAL);// 所作深度测试的类型
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
// 告诉系统对透视进行修正
}
}
在本示例中我们用到了GL10的大量的方法,现列举如下:
Ø void glClear(int mask)
清理缓冲区,也就是glClearColor或者glClearDepth等函数所指定的值来清除指定的缓冲区。参数表明哪个缓冲区需要清理,取值如下表3-1-1所示:
表3-1-1 mask取值及含义
参数mask取值 | 说明 |
GL_COLOR_BUFFER_BIT | 表明颜色缓冲区 |
GL_DEPTH_BUFFER_BIT | 表明深度缓冲区 |
GL_STENCIL_BUFFER_BIT | 表明模型缓冲区 |
Ø void glEnableClientState(int array)
开启指定功能。默认的所有客户端功能都是禁用。在OpenGL中使用glEnableClientState之后都要使用glDisableClientState来关闭或取消对应的功能。参数取值如下表3-1-2所示。
给3-1-2 array取值及含义
参数array取值 | 说明 |
GL_COLOR_ARRAY | 如果启用,颜色矩阵可以用来写入以及调用glDrawArrays方法或者glDrawElements方法时进行渲染 |
GL_NORMAL_ARRAY | 如果启用,法线矩阵可以用来写入以及调用glDrawArrays方法或者glDrawElements方法时进行渲染 |
GL_TEXTURE_COORD_ARRAY | 如果启用,纹理坐标矩阵可以用来写入以及调用glDrawArrays方法或者glDrawElements方法时进行渲染 |
GL_VERTEX_ARRAY | 如果启用,顶点矩阵可以用来写入以及调用glDrawArrays方法或者glDrawElements方法时进行渲染 |
GL_POINT_SIZE_ARRAY_OES | 如果启用,点大小矩阵控制大小以渲染点和点sprites。这时由glPointSize定义的点大小将被忽略,由点大小矩阵提供的大小将被用来渲染点和点sprites |
Ø void glDisableClientState(int array)
关闭指定功能。参数取值参看glEnableClientState()方法的array参数讲解。
Ø void glColorPointer(int size, int type, int stride, Buffer pointer)
指明渲染时使用的颜色矩阵。其中size指明每个颜色的元素数量,必须为4;type指明每个颜色元素的数据类型,允许的符号常量有GL_UNSIGNED_BYTE, GL_FIXED和GL_FLOAT,初始值为GL_FLOAT;stride指明连续的点之间的位偏移,如果stride为0时,颜色被紧密挤入矩阵,初始值为0;pointer指明包含颜色的缓冲区,如果pointer为null,则为设置缓冲区。
Ø void glLoadIdentity()
将所选的矩阵状态恢复成原始状态,即将当前点移到了屏幕中心。
Ø void glTranslatef(float x, float y, float z)
将光标移动到指定的位置。其中x指明平移向量的x坐标,y指明平移向量的y坐标,z指明平移向量的z坐标,z必须得在zNear和zFar之间,否则我们看不到图形的效果。
Ø void glRotatef(float angle, float x, float y, float z)
将物体沿指定的轴旋转。其中angle指明旋转的角度,单位为度,x指明旋转向量的x坐标,y指明旋转向量的y坐标,z指明旋转向量的z坐标
Ø void glVertexPointer(int size, int type, int stride, Buffer pointer)
用来设置顶点缓存。其中size用于描述顶点的尺寸,当使用xyz坐标系时,值取3;type顶点的类型,GL10.GL_FIXED表示固定的顶点;stride表示步长;pointer表示顶点缓存。
Ø void glDrawArrays(int mode, int first, int count)
绘制顶点。其中first表示开始位置,count表示要绘制的顶点数,mode表示绘制的模式,它的具体取值参看下表3-1-3所示 。
表3-1-3:mode取值及含义
mode取值 | 说明 |
GL10.GL_TRIANGLES | 画三角形 |
GL10.GL_TRIANGLE_STRIP | 画四边形,取点顺序如下图3.1.3中左图所示 |
GL10.GL_TRIANGLE_FAN | 画四边开,取点顺序如下图3.1.3中右图所示 |
图3.1.3 四边形取点顺序
Ø void glColor4f(float red, float green, float blue, float alpha)
设置当前所使用的颜色,之后绘制的所有内容都会使用这个颜色,即使在完全采用纹理贴图的时候,仍然可以用来调节纹理的色调。利用此方法不需要开启渲染功能。
Ø void glViewport(int x, int y, int width, int height)
设置OpenGL的场景。其中x指明场景矩形的左下角x坐标,初始值为0;y指明场景矩形的左下角y坐标,初始值为0;width指明场景的宽,如果GL上下文首次附于一个surface则宽、高为这个surface大小;height指明视口的高,如果GL上下文首次附于一个surface则宽、高为这个surface大小。
Ø void glMatrixMode(int mode)
设置当前矩阵的模式,mode允许的值参看表3-1-4所示。
表3-1-4:mode取值及含义
mode的取值 | 说明 |
GL_MODELVIEW | 应用视图矩阵堆的后续矩阵操作 |
GL_PROJECTION | 应用投射矩阵堆的后续矩阵操作 |
GL_TEXTURE | 应用纹理矩阵堆的后续矩阵操作 |
GL_MATRIX_PALETTE_OES | 启用矩阵调色板堆栈扩展,并应用矩阵调色板堆栈 |
void glFrustumf(float left, float right, float bottom, float top, float zNear, float zFar)
设置窗口的大小。其中前四个参数用于确定窗口的大小,zNear和zFar分别代表所能绘制深度的起点和终点。
void glShadeModel(int mode)
设置明暗处理模式。所谓明暗处理指的是用单一的颜色或许多不同的颜色来勾画(或填充)。其中参数mod指明一个符号常量来代表要使用的着色技术。允许的值有GL_FLAT 和GL_SMOOTH,初始值为GL_SMOOTH。
void glClearColor(float red, float green, float blue, float alpha)
设置清除屏幕时所使用的颜色。色彩范围从0.0f~1.0f,0.0f最黑,1.0f最亮。
void glClearDepthf(float depth)
指明深度缓冲区的清理值。
void glEnable(int cap)
启用服务器端GL功能。当参数取值为GL_DEPTH_TEST时表示如果启用,做深度比较和更新深度缓存。
整个程序的运行效果如下图3.1.4所示。
图3.1.4 绘制的2D图形效果
在本示例中,我们不仅讲到了基本图形(三角形、矩形)的绘制,还讲到了颜色的填充、以及图形的旋转的知识,属于一个比较综合的案例,也是我们下面绘制3D图形的基础。
3.3 绘制3D图形
我们可以通过这些2D图形构建简单的诸如“超级马里奥”等常见横版游戏,但随着手机配置的提高,越来越多的3D游戏已经可以在手机上流畅运行,这也推动着越来越多的程序开发者加入到手机3D游戏的开发中来,那么在Android系统中,我们又如何绘制漂亮的3D图形呢?
其实绘制2D图形对于OpenGL ES来说是很简单的,OpenGL ES的主要功能还是在于绘制3D图形,它从绘制简单的立体图形到设置不同的纹理、以及光照、混合等效果,可谓无所不能。在本节中我们就学习如所利用OpenGL ES来绘制3D图形
示例3.3
在屏幕上绘制一个三棱锥和一个立方体,然后给这两个图形填充上颜色,最后设置三棱锥沿Y轴旋转,立方体沿X轴旋转。整个程序的运行效果如下图3.1.5所示。
图3.1.5 简单3D图形效果
修改示例3.1中的MyRenderer类,修改后的代码如下图所示:
public class GLRenderer3D implements Renderer {
float rotateTri, rotateCube;
int one = 0x10000;
//定义六面体颜色
private IntBuffer colorBufferForCube = IntBuffer.wrap(new int[]{
0,one,0,one,
0,one,0,one,
0,one,0,one,
0,one,0,one,
one, one/2, 0, one,
one, one/2, 0, one,
one, one/2, 0, one,
one, one/2, 0, one,
one,0,0,one,
one,0,0,one,
one,0,0,one,
one,0,0,one,
one,one,0,one,
one,one,0,one,
one,one,0,one,
one,one,0,one,
0,0,one,one,
0,0,one,one,
0,0,one,one,
0,0,one,one,
one,0,one,one,
one,0,one,one,
one,0,one,one,
one,0,one,one,
});
//四棱锥颜色
private IntBuffer colorBufferForPyramid = IntBuffer.wrap(new int[]{
one,0,0,one,
0,one,0,one,
0,0,one,one,
one,0,0,one,
0,one,0,one,
0,0,one,one,
one,0,0,one,
0,one,0,one,
0,0,one,one,
one,0,0,one,
0,one,0,one,
0,0,one,one,
});
private IntBuffer PyramidBuffer = IntBuffer.wrap(new int[]{//四棱锥坐标
0,one,0,
-one,-one,0,
one,-one,one,
0,one,0,
one,-one,one,
one,-one,-one,
0,one,0,
one,-one,-one,
-one,-one,-one,
0,one,0,
-one,-one,-one,
-one,-one,one
});
private IntBuffer cubeBuffer = IntBuffer.wrap(new int[]{//六面体坐标
one,one,-one,
-one,one,-one,
one,one,one,
-one,one,one,
one,-one,one,
-one,-one,one,
one,-one,-one,
-one,-one,-one,
one,one,one,
-one,one,one,
one,-one,one,
-one,-one,one,
one,-one,-one,
-one,-one,-one,
one,one,-one,
-one,one,-one,
-one,one,one,
-one,one,-one,
-one,-one,one,
-one,-one,-one,
one, one, -one,
one, one, one,
one, -one, -one,
one, -one, one,
});
@Override
public void onDrawFrame(GL10 gl) {
// 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
// 左移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(-1.5f, 0.0f, -6.0f);
// 设置旋转
gl.glRotatef(rotateTri, 0.0f, 1.0f, 0.0f);
// 设置定点数组
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 设置颜色数组
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBufferForPyramid);
// 设置三角形顶点
gl.glVertexPointer(3, GL10.GL_FIXED, 0, PyramidBuffer);
// 绘制三角锥
for (int i = 0; i < 4; i++) {
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, i * 3, 3);
}
/* 渲染正方体 */
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
// 左移 1.5 单位,并移入屏幕 6.0
gl.glTranslatef(1.5f, 0.0f, -6.0f);
// 设置旋转
gl.glRotatef(rotateCube, 1.0f, 0.0f, 0.0f);
// 设置和绘制正方形
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBufferForCube);
gl.glVertexPointer(3, GL10.GL_FIXED, 0, cubeBuffer);
for (int i = 0; i < 6; i++) {
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, i * 4, 4);
}
// 绘制正方形结束
gl.glFinish();
// 取消顶点数组
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
// 改变旋转的角度
rotateTri += 0.5f;
rotateCube -= 0.5f;
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
float ratio = (float) width / height;
gl.glViewport(0, 0, width, height);// 设置OpenGL场景的大小
gl.glMatrixMode(GL10.GL_PROJECTION);// 设置投影矩阵
gl.glLoadIdentity();// 重置投影矩阵
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);// 设置视口的大小
gl.glMatrixMode(GL10.GL_MODELVIEW);// 选择模型观察矩阵
gl.glLoadIdentity();// 重置模型观察矩阵
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glShadeModel(GL10.GL_SMOOTH);// 启用阴影平滑
gl.glClearColor(0, 0.5f, 0.5f, 0.5f);// 黑色背景
gl.glClearDepthf(1.0f);// 设置深度缓存
gl.glEnable(GL10.GL_DEPTH_TEST);// 启用深度测试
gl.glDepthFunc(GL10.GL_LEQUAL);// 所作深度测试的类型
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);// 告诉系统对透视进行修正
}
}
在本示例中我们用到的GL10的方法如下:
Ø void glHint(int target, int mode)
用来对视图进行修正。
target用来来表明被控制的行为,当取值为GL_PERSPECTIVE_CORRECTION_HINT时用来指定颜色和纹理坐标的插值质量。
mode用来表明想要执行的行为,取值如下表3-1-5所示。
表3-1-5 mode取值及含义
mode取值 | 说明 |
GL_FASTEST | 使用速度最快的模式 |
GL_NICEST | 使用质量最好的模式. |
GL_DONT_CAR | 由驱动设备来决定 |
Ø void glDepthFunc(int func)
设置所做深度测试的类型。
任务实训部分
1:绘制一个带纹理的旋转的立方体
训练技能点
Ø 基本3D图形的绘制
Ø 纹理的使用
Ø 3D图形的旋转控制
需求说明
3D游戏相较于普通2D游戏的优点就在于可以让玩家不断变换视角,可以360度查看游戏角色,这里我们用一个立方体模拟一个3D游戏角色,使之不断旋转来模拟查看其各个方位不同的状态。程序最终运行效果如下图3.2.1所示。
图3.2.1 旋转的立方体
实现思路:
核心代码如下
public class GLRotateCubeRender implements Renderer
{
float xrot, yrot, zrot;
int texture = -1;
int one = 0x10000;
IntBuffer vertices = IntBuffer.wrap(new int[]{
-one,-one,one,
one,-one,one,
one,one,one,
-one,one,one,
-one,-one,-one,
-one,one,-one,
one,one,-one,
one,-one,-one,
-one,one,-one,
-one,one,one,
one,one,one,
one,one,-one,
-one,-one,-one,
one,-one,-one,
one,-one,one,
-one,-one,one,
one,-one,-one,
one,one,-one,
one,one,one,
one,-one,one,
-one,-one,-one,
-one,-one,one,
-one,one,one,
-one,one,-one,
});
//对立方体的每一个面所设置的纹理映射数据
IntBuffer texCoords = IntBuffer.wrap(new int[]{
one,0,0,0,0,one,one,one,
0,0,0,one,one,one,one,0,
one,one,one,0,0,0,0,one,
0,one,one,one,one,0,0,0,
0,0,0,one,one,one,one,0,
one,0,0,0,0,one,one,one,
});
ByteBuffer indices = ByteBuffer.wrap(new byte[]{
0,1,3,2,
4,5,7,6,
8,9,11,10,
12,13,15,14,
16,17,19,18,
20,21,23,22,
});
@Override
public void onDrawFrame(GL10 gl)
{
// 清除屏幕和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 重置当前的模型观察矩阵
gl.glLoadIdentity();
gl.glTranslatef(0.0f, 0.0f, -5.0f);
//设置3个方向的旋转
gl.glRotatef(xrot, 1.0f, 0.0f, 0.0f);
gl.glRotatef(yrot, 0.0f, 1.0f, 0.0f);
gl.glRotatef(zrot, 0.0f, 0.0f, 1.0f);
// 绑定纹理
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
//纹理和四边形对应的顶点
gl.glVertexPointer(3, GL10.GL_FIXED, 0, vertices);
gl.glTexCoordPointer(2, GL10.GL_FIXED, 0, texCoords);
//绘制
gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, 24, GL10.GL_UNSIGNED_BYTE, indices);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
xrot+=0.5f;
yrot+=0.6f;
zrot+=0.3f;
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
float ratio = (float) width / height;
//设置OpenGL场景的大小
gl.glViewport(0, 0, width, height);
//设置投影矩阵
gl.glMatrixMode(GL10.GL_PROJECTION);
//重置投影矩阵
gl.glLoadIdentity();
// 设置视口的大小
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
// 选择模型观察矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW);
// 重置模型观察矩阵
gl.glLoadIdentity();
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
// 黑色背景
gl.glClearColor(0, 0, 0, 0);
gl.glEnable(GL10.GL_CULL_FACE);
// 启用阴影平滑
gl.glShadeModel(GL10.GL_SMOOTH);
// 启用深度测试
gl.glEnable(GL10.GL_DEPTH_TEST);
//启用纹理映射
gl.glClearDepthf(1.0f);
//深度测试的类型
gl.glDepthFunc(GL10.GL_LEQUAL);
//精细的透视修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
//允许2D贴图,纹理
gl.glEnable(GL10.GL_TEXTURE_2D);
IntBuffer intBuffer = IntBuffer.allocate(1);
// 创建纹理
gl.glGenTextures(1, intBuffer);
texture = intBuffer.get();
// 设置要使用的纹理
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
//生成纹理
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, GLImage.mBitmap, 0);
// 线形滤波
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
}
}
class GLImage
{
public static Bitmap mBitmap;
public static void load(Resources resources)
{
mBitmap = BitmapFactory.decodeResource(resources, R.drawable.img);
}
}
提示:
本例中用到两个纹理处理的方法,语法及解释如下:
Ø void glGenTextures(int n, IntBuffer textures)
创建纹理,用于通知OpenGL我们所要生成的纹理的名字。其中参数n表示要载入的纹理的个数,textures表示纹理的名字。
Ø void glBindTexture(int target, int texture)
设置要使用的纹理,绑定纹理操作必须在绘图之前完成。用于通知OpenGL将纹理名字texture绑定到纹理目标上。其中参数target描述了要绑定的纹理的类型,texture表示纹理的名字。
巩固练习
一、简答题
1. 简述OpenGL与OpenGL ES的关系及区别。
2. 简述在Android中使用OpenGL ES的基本步骤。
二、上机练习
利用基本图形组合的形式,采用OpenGL ES的知识绘制一个简单的机器人,然后为机器人添加走动的功能(此步选做)。