Android OpenGL ES 开发教程(6):GLSurfaceView

来源:互联网 发布:根据域名查ip 编辑:程序博客网 时间:2024/05/23 12:09

http://www.imobilebbs.com/wordpress/?p=1889

Android OpenGL ES 相关的包主要定义在:

  • javax.microedition.khronos.opengles    GL 绘图指令
  • javax.microedition.khronos.egl               EGL 管理Display, surface等
  • android.opengl    Android GL辅助类,连接OpenGL 与Android View,Activity
  • javax.nio Buffer类

其中GLSurfaceView 为android.opengl  包中核心类:

  • 起到连接OpenGL ES与Android 的View层次结构之间的桥梁作用。
  • 使得Open GL ES库适应于Anndroid系统的Activity生命周期。
  • 使得选择合适的Frame buffer像素格式变得容易。
  • 创建和管理单独绘图线程以达到平滑动画效果。
  • 提供了方便使用的调试工具来跟踪OpenGL ES函数调用以帮助检查错误。

使用过Java ME ,JSR 239 开发过OpenGL ES可以看到 Android 包javax.microedition.khronos.egl ,javax.microedition.khronos.opengles 和JSR239 基本一致,因此理论上不使用android.opengl 包中的类也可以开发Android上OpenGL ES应用,但此时就需要自己使用EGL来管理Display,Context, Surfaces 的创建,释放,捆绑,可以参见Android OpenGL ES 开发教程(5):关于EGL 。

使用EGL 实现GLSurfaceView一个可能的实现如下:

帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
classGLSurfaceView extendsSurfaceView
 implementsSurfaceHolder.Callback, Runnable {
 publicGLSurfaceView(Context context) {
 super(context);
 mHolder = getHolder();
 mHolder.addCallback(this);
 mHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
 }
 
 publicvoid setRenderer(Renderer renderer) {
 mRenderer = renderer;
 }
 
 publicvoid surfaceCreated(SurfaceHolder holder) {
 }
 
 publicvoid surfaceDestroyed(SurfaceHolder holder) {
 running = false;
 try{
 thread.join();
 }catch(InterruptedException e) {
 }
 thread = null;
 }
 
 publicvoid surfaceChanged(SurfaceHolder holder,
 intformat, intw, inth) {
 synchronized(this){
 mWidth = w;
 mHeight = h;
 thread = newThread(this);
 thread.start();
 }
 }
 
 publicinterface Renderer {
 voidEGLCreate(SurfaceHolder holder);
 voidEGLDestroy();
 intInitialize(intwidth, intheight);
 voidDrawScene(intwidth, intheight);
 }
 
 publicvoid run() {
 synchronized(this) {
 mRenderer.EGLCreate(mHolder);
 mRenderer.Initialize(mWidth, mHeight);
 
 running=true;
 while(running) {
 mRenderer.DrawScene(mWidth, mHeight);
 }
 
 mRenderer.EGLDestroy();
 }
 }
 
 privateSurfaceHolder mHolder;
 privateThread thread;
 privateboolean running;
 privateRenderer mRenderer;
 privateint mWidth;
 privateint mHeight;
 
}
 
classGLRenderer implementsGLSurfaceView.Renderer {
 publicGLRenderer() {
 }
 
 publicint Initialize(intwidth, intheight){
 gl.glClearColor(1.0f,0.0f,0.0f,0.0f);
 
 return1;
 }
 
 publicvoid DrawScene(intwidth, intheight){
 gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
 
 egl.eglSwapBuffers(eglDisplay, eglSurface);
 }
 
 publicvoid EGLCreate(SurfaceHolder holder){
 int[] num_config = newint[1];
 EGLConfig[] configs = newEGLConfig[1];
 int[] configSpec = {
 EGL10.EGL_RED_SIZE,            8,
 EGL10.EGL_GREEN_SIZE,        8,
 EGL10.EGL_BLUE_SIZE,        8,
 
 EGL10.EGL_SURFACE_TYPE,     EGL10.EGL_WINDOW_BIT,
 EGL10.EGL_NONE
 };
 
 this.egl = (EGL10) EGLContext.getEGL();
 
 eglDisplay = this.egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
 this.egl.eglInitialize(eglDisplay,null);
 
 this.egl.eglChooseConfig(eglDisplay, configSpec,
 configs,1, num_config);
 
 eglConfig = configs[0];
 eglContext = this.egl.eglCreateContext(eglDisplay, eglConfig,
 EGL10.EGL_NO_CONTEXT,null);
 
 eglSurface = this.egl.eglCreateWindowSurface(eglDisplay,
 eglConfig, holder, null);
 
 this.egl.eglMakeCurrent(eglDisplay, eglSurface,
 eglSurface, eglContext);
 
 gl = (GL10)eglContext.getGL();
 }
 
 publicvoid EGLDestroy(){
 if(eglSurface != null) {
 egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
 EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
 egl.eglDestroySurface(eglDisplay, eglSurface);
 eglSurface = null;
 }
 if(eglContext != null) {
 egl.eglDestroyContext(eglDisplay, eglContext);
 eglContext = null;
 }
 if(eglDisplay != null) {
 egl.eglTerminate(eglDisplay);
 eglDisplay = null;
 }
 }
 
 privateEGL10 egl;
 privateGL10 gl;
 privateEGLDisplay eglDisplay;
 privateEGLConfig  eglConfig;
 privateEGLContext eglContext;
 privateEGLSurface eglSurface;
}

可以看到需要派生SurfaceView ,并手工创建,销毁Display,Context ,工作繁琐。

使用GLSurfaceView 内部提供了上面类似的实现,对于大部分应用只需调用一个方法来设置OpenGLView用到的GLSurfaceView.Renderer.

帮助
1
publicvoid  setRenderer(GLSurfaceView.Renderer renderer)

GLSurfaceView.Renderer定义了一个统一图形绘制的接口,它定义了如下三个接口函数:

帮助
1
2
3
4
5
6
// Called when the surface is created or recreated.
publicvoid onSurfaceCreated(GL10 gl, EGLConfig config)
// Called to draw the current frame.
publicvoid onDrawFrame(GL10 gl)
// Called when the surface changed size.
publicvoid onSurfaceChanged(GL10 gl, intwidth, intheight)
  • onSurfaceCreated : 在这个方法中主要用来设置一些绘制时不常变化的参数,比如:背景色,是否打开 z-buffer等。
  • onDrawFrame: 定义实际的绘图操作。
  • onSurfaceChanged: 如果设备支持屏幕横向和纵向切换,这个方法将发生在横向<->纵向互换时。此时可以重新设置绘制的纵横比率。

如果有需要,也可以通过函数来修改GLSurfaceView一些缺省设置:

  • setDebugFlags(int) 设置Debug标志。
  • setEGLConfigChooser (boolean) 选择一个Config接近16bitRGB颜色模式,可以打开或关闭深度(Depth)Buffer ,缺省为RGB_565 并打开至少有16bit 的 depth Buffer.
  • setEGLConfigChooser(EGLConfigChooser)  选择自定义EGLConfigChooser。
  • setEGLConfigChooser(int, int, int, int, int, int) 指定red ,green, blue, alpha, depth ,stencil 支持的位数,缺省为RGB_565 ,16 bit depth buffer.

GLSurfaceView 缺省创建为RGB_565 颜色格式的Surface ,如果需要支持透明度,可以调用getHolder().setFormat(PixelFormat.TRANSLUCENT).

GLSurfaceView 的渲染模式有两种,一种是连续不断的更新屏幕,另一种为on-demand ,只有在调用requestRender()  在更新屏幕。 缺省为RENDERMODE_CONTINUOUSLY 持续刷新屏幕。