橘子味的心
标题:10.7 Android 3D绘图简介

3D图像的绘制使用的是 OpenGL ES,所以我们先介绍 OpenGL ES。

OpenGL 是一组跨平台的 3D 图像处理 API,OpenGL ES 是 OpenGL 的嵌入式版本,Android 系统从 Android 1.0 开始支持 OpenGL ES 1.0 和 1.1,自 Android 2.2(API Level 8)开始,Android 框架开始支持 OpenGL 2.0 API。

在这里仅介绍其使用方法。详细资料可以查询 Android SDK 的相关文档。

使用 OpenGL ES API 绘制 3D 图像有两个基础的相关类,一个是 GLSurfaceView 类,另一个是 GLSurfaceView.Renderer 接口。

1)GLSurfaceView 类

GLSurfaceView 类是 SurfaceView 的子类,使用内嵌的 Surface 进行 OpenGL 绘图渲染。GLSurfaceView 提供以下功能:
  • 管理 Surface,Surface 是一块内存,可以被加载到 View 视图中。
  • 管理一个 EGL 显示,能够使用 OpenGL 把内容渲染到 Surface 上。
  • 接受用户自定义渲染器用于实际渲染。
  • 使渲染器在单独的线程中运行,与更新 UI 的线程相分离。
  • 支持按需渲染(on-demand rendering)和连续渲染(continuous rendering)。
  • 提供一些可选工具,如 OpenGL 调用的跟踪调试和错误检查等。

2)GLSurfaceView.Renderer 接口

GLSurfaceView.Renderer 接口定义了使用 OpenGL 绘图时所需的方法。该接口通过 GLSurfaceView.setRenderer() 与 GLSurfaceView 关联在一起。

该接口实现以下三个方法。
  • onSurfaceCreated():当创建 GLSurfaceView 对象后,该方法被系统调用一次。通常在该方法中设置 OpenGL 环境的相关参数,初始化 OpenGL 图形对象等。
  • onDrawFrame():GLSurfaceView 对象每一次重绘时系统都会调用该方法。该方法应该执行具体的绘图工作。
  • onSurfaceChanged():当 GLSurfaceView 对象的几何外形改变时,包括 GLSurfaceView 的尺寸发生改变、设备屏幕的方向发生改变等情况,该方法被系统调用。

实例 OpenGLDemo 演示了在 Activity 中使用 GLSurfaceView 和 GLSurfaceView.Renderer 合作绘制三维图形的过程。

该实例绘制了一个不断旋转的立方体,运行效果如图 1 所示。

实例OpenGLDemo的运行效果
图 1  实例OpenGLDemo的运行效果

该立方体为 MyCube 类的对象。MyCube.java 的代码如下:
  1. import java.nio.ByteBuffer;
  2. import java.nio.ByteOrder;
  3. import java.nio.IntBuffer;
  4.  
  5. import javax.microedition.khronos.opengles.GL10;
  6.  
  7. class MyCube {
  8.  
  9. private IntBuffer vertexBuffer;
  10. private IntBuffer colorBuffer;
  11. private ByteBuffer indexBuffer;
  12.  
  13. public MyCube() {
  14. int one = 65536;
  15. int vertex[] = {
  16. -one, -one, -one, one, -one, -one,
  17. one, one, -one,
  18. -one, one, -one,
  19. -one, -one, one,
  20. one, -one, one,
  21. one, one, one,
  22. -one, one, one
  23. };
  24. int colors[] = {
  25. 0, 0, 0, one,
  26. one, 0, 0, one,
  27. one, one, 0, one,
  28. 0, one, 0, one,
  29. 0, 0, one, one,
  30. one, 0, one, one,
  31. one, one, one, one,
  32. 0, one, one, one
  33. };
  34. byte index[] = {
  35. 0, 4, 5, 0, 5, 1,
  36. 1, 5, 6, 1, 6, 2,
  37. 2, 6, 7, 2, 7, 3,
  38. 3, 7, 4, 3, 4, 0,
  39. 4, 7, 6, 4, 6, 5,
  40. 3, 0, 1, 3, 1, 2
  41. };
  42.  
  43. ByteBuffer vbb = ByteBuffer.allocateDirect(vertex.length * 4);
  44. vbb.order(ByteOrder.nativeOrder());
  45. vertexBuffer = vbb.asIntBuffer();
  46. vertexBuffer.put(vertex);
  47. vertexBuffer.position(0);
  48.  
  49. ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length);
  50. cbb.order(ByteOrder.nativeOrder());
  51. colorBuffer = cbb.asIntBuffer();
  52. colorBuffer.put(colors);
  53. colorBuffer.position(0);
  54.  
  55. indexBuffer = ByteBuffer.allocateDirect(index.length);
  56. indexBuffer.put(index);
  57. indexBuffer.position(0);
  58. }
  59.  
  60. public void draw(GL10 gl) {
  61. gl.glFrontFace(GL10.GL_CW);
  62. gl.glVertexPointer(3, GL10.GL_FIXED, 0, vertexBuffer);
  63. gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer);
  64. gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, indexBuffer);
  65. }
  66. }
该立方体被显示在 GLSurfaceView 对象中,由 GLSurfaceView.Renderer 接口绘制。GLSurfaceView 在主 Activity 的 onCreate() 方法中被创建,相关代码如下:
  1. package introduction.android.openglDemo;
  2.  
  3. import android.app.Activity;
  4. import android.opengl.GLSurfaceView;
  5. import android.os.Bundle;
  6.  
  7. public class OpenGLDemoActivity extends Activity {
  8. private GLSurfaceView myGLSurfaceView;
  9.  
  10. /**
  11. * Called when the activity is first created.
  12. */
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. myGLSurfaceView = new GLSurfaceView(this);
  17. myGLSurfaceView.setRenderer(new CubeRenderer());
  18. setContentView(myGLSurfaceView);
  19. }
  20.  
  21. @Override
  22. protected void onResume() {
  23. super.onResume();
  24. myGLSurfaceView.onResume();
  25. }
  26.  
  27. @Override
  28. protected void onPause() {
  29. super.onPause();
  30. myGLSurfaceView.onPause();
  31. }
  32. }
其中:

myGLSurfaceView.setRenderer(new CubeRenderer());

指定了 GLSurfaceView 的渲染器为 CubeRenderer,由该渲染器控制图像绘制过程。渲染器被定义在 CubeRenderer.java 中,具体代码如下:
  1. package introduction.android.openglDemo;
  2.  
  3. import android.opengl.GLSurfaceView;
  4.  
  5. import javax.microedition.khronos.egl.EGLConfig;
  6. import javax.microedition.khronos.opengles.GL10;
  7.  
  8. class CubeRenderer implements GLSurfaceView.Renderer {
  9. private MyCube myCube;
  10. private float roate;
  11.  
  12. public CubeRenderer() {
  13. myCube = new MyCube();
  14. }
  15.  
  16. public void onDrawFrame(GL10 gl) {
  17. //填充屏幕
  18. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT)
  19. //设置模型视景矩阵为当前操作矩阵
  20. gl.glMatrixMode(GL10.GL_MODELVIEW);
  21. //将坐标原点移动到屏幕中心
  22. gl.glLoadIdentity();
  23. //移动坐标系
  24. gl.glTranslatef(0, 0, -3.0f);
  25. //在Y轴方向旋转坐标系
  26. gl.glRotatef(roate, 0, 1, 0);
  27. //在X轴方向旋转坐标系
  28. gl.glRotatef(roate * 0.25f, 1, 0, 0);
  29. //开启顶点坐标
  30. gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
  31. //开启颜色
  32. gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
  33. //绘制图形
  34. myCube.draw(gl);
  35. roate += 1.0f;
  36. }
  37.  
  38. public void onSurfaceChanged(GL10 gl, int width, int height) {
  39. gl.glViewport(0, 0, width, height);
  40. float ratio = (float) width / height;
  41. gl.glMatrixMode(GL10.GL_PROJECTION);
  42. gl.glLoadIdentity();
  43. gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
  44. }
  45.  
  46. public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  47. gl.glEnable(GL10.GL_CULL_FACE);
  48. gl.glClearColor(0.5F, 0.5F, 0.5F, 1.0F);
  49. }
  50. }