第 9 章 2D 图形接口的使用
10.3 渲染器的实现
10.1 使用 OpenGL 图形接口的程序结构 10.2 基本的绘制
10.3 渲染器的实现 10.4 3D 动画效果的实现
10.1 使用OpenGL图形接口的程序结构。
在 Android 中,可以直接支持 3D 图形的绘制,主要使用 OpenGL 标准的类 javax.microedition.khronos.egl,
但是需要结合 Android GUI 系统使用。Android 中 OpenGL 接口使用的结构如图所示:
图 Android OpenGL 绘图接口结构
在使用 3D 的图形 API 方面,主要的步骤通常如下所示:
1.扩展实现 android.view.GLSurfaceView 类。
2.扩展实现 android.opengl.GLSurfaceView 中的 Renderer(渲染器)。
3.实现 GLSurfaceView::Renderer 中的 onDrawFrame()等函数。
android.opengl.GLSurfaceView 扩 展 了 android.view.SurfaceView , android. view.SurfaceView 扩 展 了 android.view.View,因此 GLSurfaceView 本身可以作为 android. view.View 来使用。
GLSurfaceView::Renderer 是一个接口,其中主要定义了以下几个方法:
abstract void onDrawFrame(GL10 gl) // 绘制当前帧
abstract void onSurfaceChanged(GL10 gl, int width, int height)
// Surface 变化时调用
abstract void onSurfaceCreated(GL10 gl, EGLConfig config)
// Surface 创建时调用
各个方法的参数 GL10 是 javax.microedition.khronos.egl 包中的通用函数。GLSurface View::Renderer 中的 onSurfaceChanged() 和 onSurfaceCreated() 方 法 实 际 上 是 和 SurfaceView 中 的 两 个 方 法 对 应 的 。 实 现 的 GLSurfaceView::Renderer,通过 GLSurfaceView 的 setRenderer()方法将其设置到 GLSurfaceView 中。
在 ApiDemo 的示例程序中,android/apis/graphics/中的 GLSurfaceViewActivity、TouchRotateActivity、
TriangleActivity 等程序和 spritetext/及/Kube/目录中的程序是 OpenGL 的示例程序。
10.2 基本的绘制
参考示例程序:Touch Rotate(Graphics=>OpenGL ES=>Touch Rotate)
源代码:android/apis/graphics/TouchRotateActivity.java Touch Rotate 程序的运行结果如图所示:
图 Touch Rotate 程序的运行结果
本程序显示了一个可以旋转的立方体,TouchRotate Activity 类的结构如下所示:
public class TouchRotateActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
mGLSurfaceView = new TouchSurfaceView(this); // 建立 GLSurfaceView setContentView(mGLSurfaceView); // 设置 View 到活动中 mGLSurfaceView.requestFocus(); // 配置 GLSurfaceView mGLSurfaceView.setFocusableInTouchMode(true);
}
class TouchSurfaceView extends GLSurfaceView { public TouchSurfaceView(Context context) { super(context);
mRenderer = new CubeRenderer(); // 建立渲染器 setRenderer(mRenderer); // 设置渲染器 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
private class CubeRenderer implements GLSurfaceView.Renderer { // 实现渲染器接口
public void onDrawFrame(GL10 gl) { // 调用 OpenGL 的标准接口进行操作
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -3.0f);
gl.glRotatef(mAngleX, 0, 1, 0); // 对绘制的图形进行旋转 gl.glRotatef(mAngleY, 1, 0, 0);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
mCube.draw(gl); // 调用 draw()进行绘制 }
} }
CubeRenderer 渲染器中的 onSurfaceChanged()和 onSurfaceCreated()两个函数进行了 Surface 变化及创建时的 操作。
@Override public boolean onTrackballEvent(MotionEvent e) { mRenderer.mAngleX += e.getX() * TRACKBALL_SCALE_FACTOR;
mRenderer.mAngleY += e.getY() * TRACKBALL_SCALE_FACTOR;
requestRender();
return true;
}
@Override public boolean onTouchEvent(MotionEvent e) { float x = e.getX();
在 Android 中,也可以不扩展 GLSurfaceView 类,只是实现 GLSurfaceView::Renderer 接口。实现渲染器可 以在 GLSurfaceView 中直接使用。
图 立方体和透明的立方体
Cube 实 现 的 是 一 个 使 用 OpenGL 绘 制 的 立 方 体 , CubeRenderer 表 示 基 于 立 方 体 实 现 的 渲 染 器 , GLSurfaceViewActivity 和 TranslucentGLSurfaceViewActivity 分别是图中左右两个效果的界面。
CubeRenderer.java 的内容:
class CubeRenderer implements GLSurfaceView.Renderer { public CubeRenderer(boolean useTranslucentBackground) { mTranslucentBackground = useTranslucentBackground;
mCube = new Cube();
}
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -3.0f);
gl.glRotatef(mAngle, 0, 1, 0);
gl.glRotatef(mAngle*0.25f, 1, 0, 0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
mCube.draw(gl);
private boolean mTranslucentBackground;
private Cube mCube;
private float mAngle;
}
AndroidManifest.xml 中两个活动的内容:
<activity android:name=".graphics.GLSurfaceViewActivity"
android:label="Graphics/OpenGL ES/GLSurfaceView"
<activity android:name=".graphics.TranslucentGLSurfaceViewActivity"
android:label="Graphics/OpenGL ES/Translucent GLSurfaceView"
2 个 程 序 主 要 的 区 别 是 TranslucentGLSurfaceViewActivity 通 过 android:theme 将 背 景 颜 色 设 置 成 了 Theme.Translucent,表示透明的背景。
GLSurfaceViewActivity.java 的内容如下所示:
public class GLSurfaceViewActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
mGLSurfaceView.setRenderer(new CubeRenderer(false));
setContentView(mGLSurfaceView);
} }
TranslucentGLSurfaceViewActivity.java 的内容:
public class TranslucentGLSurfaceViewActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
mGLSurfaceView = new GLSurfaceView(this);
// 使用 RGB8888 的像素格式
mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
// 定义下层为透明
mGLSurfaceView.setRenderer(new CubeRenderer(true));
// 设置 Surface 的 Alpha 通道
mGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
// 设置 Surface 透明
setContentView(mGLSurfaceView);
} }
以上的 2 个程序中,重了后者设置了透明效果外基本相同,在这 2 个实现中,不需要重新实现 GLSurfaceView,
而只是实现 GLSurfaceView 中::Renderer 即可,
10.4 3D动画效果的实现
OpenGL 本身可以强大的实现动画的效果,在 Android 中实现动画,实际上只是将 OpenGL 动画需要使用 的元素在 Android 中重新实现。
参考示例程序:Touch Rotate(Graphics=>OpenGL ES=>Textured Triangle)
源代码:src/com/example/android/apis/graphics/TriangleActivity.java src/com/example/android/apis/graphics/TriangleRenderer.java
Touch Rotate 程序的运行结果如图所示:
TriangleActivity.java 的内容如下所示:
public class TriangleActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(this);
mGLView.setEGLConfigChooser(false);
mGLView.setRenderer(new TriangleRenderer(this));
setContentView(mGLView);
程序的在 onPause()和 onResume()中调用 View 的对应函数。
TriangleRenderer.java 的主要函数的内容如下所示:
public TriangleRenderer(Context context) { mContext = context;
mTriangle = new Triangle();
}
public void onDrawFrame(GL10 gl) { gl.glDisable(GL10.GL_DITHER);
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 开始绘制
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);