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

jerry OpenGL ES 2015年11月25日 收藏

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. class GLSurfaceView extends SurfaceView
  2. implements SurfaceHolder.Callback, Runnable {
  3. public GLSurfaceView(Context context) {
  4. super(context);
  5. mHolder = getHolder();
  6. mHolder.addCallback(this);
  7. mHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
  8. }
  9.  
  10. public void setRenderer(Renderer renderer) {
  11. mRenderer = renderer;
  12. }
  13.  
  14. public void surfaceCreated(SurfaceHolder holder) {
  15. }
  16.  
  17. public void surfaceDestroyed(SurfaceHolder holder) {
  18. running = false;
  19. try {
  20. thread.join();
  21. } catch (InterruptedException e) {
  22. }
  23. thread = null;
  24. }
  25.  
  26. public void surfaceChanged(SurfaceHolder holder,
  27. int format, int w, int h) {
  28. synchronized(this){
  29. mWidth = w;
  30. mHeight = h;
  31. thread = new Thread(this);
  32. thread.start();
  33. }
  34. }
  35.  
  36. public interface Renderer {
  37. void EGLCreate(SurfaceHolder holder);
  38. void EGLDestroy();
  39. int Initialize(int width, int height);
  40. void DrawScene(int width, int height);
  41. }
  42.  
  43. public void run() {
  44. synchronized(this) {
  45. mRenderer.EGLCreate(mHolder);
  46. mRenderer.Initialize(mWidth, mHeight);
  47.  
  48. running=true;
  49. while (running) {
  50. mRenderer.DrawScene(mWidth, mHeight);
  51. }
  52.  
  53. mRenderer.EGLDestroy();
  54. }
  55. }
  56.  
  57. private SurfaceHolder mHolder;
  58. private Thread thread;
  59. private boolean running;
  60. private Renderer mRenderer;
  61. private int mWidth;
  62. private int mHeight;
  63.  
  64. }
  65.  
  66. class GLRenderer implements GLSurfaceView.Renderer {
  67. public GLRenderer() {
  68. }
  69.  
  70. public int Initialize(int width, int height){
  71. gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
  72.  
  73. return 1;
  74. }
  75.  
  76. public void DrawScene(int width, int height){
  77. gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  78.  
  79. egl.eglSwapBuffers(eglDisplay, eglSurface);
  80. }
  81.  
  82. public void EGLCreate(SurfaceHolder holder){
  83. int[] num_config = new int[1];
  84. EGLConfig[] configs = new EGLConfig[1];
  85. int[] configSpec = {
  86. EGL10.EGL_RED_SIZE,            8,
  87. EGL10.EGL_GREEN_SIZE,        8,
  88. EGL10.EGL_BLUE_SIZE,        8,
  89.  
  90. EGL10.EGL_SURFACE_TYPE,     EGL10.EGL_WINDOW_BIT,
  91. EGL10.EGL_NONE
  92. };
  93.  
  94. this.egl = (EGL10) EGLContext.getEGL();
  95.  
  96. eglDisplay = this.egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
  97. this.egl.eglInitialize(eglDisplay, null);
  98.  
  99. this.egl.eglChooseConfig(eglDisplay, configSpec,
  100. configs, 1, num_config);
  101.  
  102. eglConfig = configs[0];
  103. eglContext = this.egl.eglCreateContext(eglDisplay, eglConfig,
  104. EGL10.EGL_NO_CONTEXT, null);
  105.  
  106. eglSurface = this.egl.eglCreateWindowSurface(eglDisplay,
  107. eglConfig, holder, null);
  108.  
  109. this.egl.eglMakeCurrent(eglDisplay, eglSurface,
  110. eglSurface, eglContext);
  111.  
  112. gl = (GL10)eglContext.getGL();
  113. }
  114.  
  115. public void EGLDestroy(){
  116. if (eglSurface != null) {
  117. egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
  118. EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
  119. egl.eglDestroySurface(eglDisplay, eglSurface);
  120. eglSurface = null;
  121. }
  122. if (eglContext != null) {
  123. egl.eglDestroyContext(eglDisplay, eglContext);
  124. eglContext = null;
  125. }
  126. if (eglDisplay != null) {
  127. egl.eglTerminate(eglDisplay);
  128. eglDisplay = null;
  129. }
  130. }
  131.  
  132. private EGL10 egl;
  133. private GL10 gl;
  134. private EGLDisplay eglDisplay;
  135. private EGLConfig  eglConfig;
  136. private EGLContext eglContext;
  137. private EGLSurface eglSurface;
  138. }
  139.  

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

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

  1. public void  setRenderer(GLSurfaceView.Renderer renderer)

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

  1. // Called when the surface is created or recreated.
  2. public void onSurfaceCreated(GL10 gl, EGLConfig config)
  3. // Called to draw the current frame.
  4. public void onDrawFrame(GL10 gl)
  5. // Called when the surface changed size.
  6. public void onSurfaceChanged(GL10 gl, int width, int height)
  • 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 持续刷新屏幕。