GLSurfaceView

来源:互联网 发布:直播系统源码 编辑:程序博客网 时间:2024/04/29 21:59

GLSurfaceView是一个视图,继承至SurfaceView,它内嵌的surface专门负责OpenGL渲染。

        GLSurfaceView提供了下列特性:
            1> 管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上。
            2> 管理一个EGL display,它能让opengl把内容渲染到上述的surface上。
            3> 用户自定义渲染器(render)。
            4> 让渲染器在独立的线程里运作,和UI线程分离。
            5> 支持按需渲染(on-demand)和连续渲染(continuous)。
            6> 一些可选工具,如调试。
        
使用GLSurfaceView
        通常会继承GLSurfaceView,并重载一些和用户输入事件有关的方法。如果你不需要重载事件方法,GLSurfaceView也可以直接使用,你可以使用set方法来为该类提供自定义的行为。例如,GLSurfaceView的渲染被委托给渲染器在独立的渲染线程里进行,这一点和普通视图不一样,setRenderer(Renderer)设置渲染器。
        
初始化GLSurfaceView
        初始化过程其实仅需要你使用setRenderer(Renderer)设置一个渲染器(render)。当然,你也可以修改GLSurfaceView一些默认配置。
            * setDebugFlags(int)
            * setEGLConfigChooser(boolean)
            * setEGLConfigChooser(EGLConfigChooser)
            * setEGLConfigChooser(int, int, int, int, int, int)
            * setGLWrapper(GLWrapper) 

定制android.view.Surface
        GLSurfaceView默认会创建像素格式为PixelFormat.RGB_565的surface。如果需要透明效果,调用getHolder().setFormat(PixelFormat.TRANSLUCENT)。透明(TRANSLUCENT)的surface的像素格式都是32位,每个色彩单元都是8位深度,像素格式是设备相关的,这意味着它可能是ARGB、RGBA或其它。
        
选择EGL配置
        Android设备往往支持多种EGL配置,可以使用不同数目的通道(channel),也可以指定每个通道具有不同数目的位(bits)深度。因此,在渲染器工作之前就应该指定EGL的配置。GLSurfaceView默认EGL配置的像素格式为RGB_656,16位的深度缓存(depth buffer),默认不开启遮罩缓存(stencil buffer)。
        如果你要选择不同的EGL配置,请使用setEGLConfigChooser方法中的一种。
        
调试行为
        你可以调用调试方法setDebugFlags(int)或setGLWrapper(GLSurfaceView.GLWrapper)来自定义GLSurfaceView一些行为。在setRenderer方法之前或之后都可以调用调试方法,不过最好是在之前调用,这样它们能立即生效。
        
设置渲染器
        总之,你必须调用setRenderer(GLSurfaceView.Renderer)来注册一个GLSurfaceView.Renderer渲染器。渲染器负责真正的GL渲染工作。
        
渲染模式
        渲染器设定之后,你可以使用setRenderMode(int)指定渲染模式是按需(on demand)还是连续(continuous)。默认是连续渲染。
        
Activity生命周期
        Activity窗口暂停(pause)或恢复(resume)时,GLSurfaceView都会收到通知,此时它的onPause方法和onResume方法应该被调用。这样做是为了让GLSurfaceView暂停或恢复它的渲染线程,以便它及时释放或重建OpenGL的资源。
        
事件处理
        为了处理事件,一般都是继承GLSurfaceView类并重载它的事件方法。但是由于GLSurfaceView是多线程操作,所以需要一些特殊的处理。由于渲染器在独立的渲染线程里,你应该使用Java的跨线程机制跟渲染器通讯。queueEvent(Runnable)方法就是一种相对简单的操作,例如下面的例子。
        
[java] view plain copy
  1. class MyGLSurfaceView extends GLSurfaceView {  
  2.     private MyRenderer mMyRenderer;  
  3.   
  4.         public void start() {  
  5.             mMyRenderer = ...;  
  6.             setRenderer(mMyRenderer);  
  7.         }  
  8.   
  9.   
  10.         public boolean onKeyDown(int keyCode, KeyEvent event) {  
  11.   
  12.             if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {  
  13.                 queueEvent(new Runnable() {  
  14.                     // 这个方法会在渲染线程里被调用  
  15.                          public void run() {  
  16.                              mMyRenderer.handleDpadCenter();  
  17.                          }});  
  18.                      return true;  
  19.                  }  
  20.   
  21.                  return super.onKeyDown(keyCode, event);  
  22.             }  
  23.       }  
  24. }  
 
        (注:如果在UI线程里调用渲染器的方法,很容易收到“call to OpenGL ES API with no current context”的警告,典型的误区就是在键盘或鼠标事件方法里直接调用opengl es的API,因为UI事件和渲染绘制在不同的线程里。更甚者,这种情况下调用glDeleteBuffers这种释放资源的方法,可能引起程序的崩溃,因为UI线程想释放它,渲染线程却要使用它。)
实例说明:
GLSurfaceView是一个很好的基类对于构建一个使用OpenGL ES进行部分或全部渲染的应用程序。一个2D或3D的动作游戏就是一个很好的例子,例如一个2D或3D的可视化应用如谷歌地图。
 
以下是一个简单的GLSurfaceView的应用,一个最简单的OpenGL ES应用代码如下:
 
[java] view plain copy
  1. package  com.javaeye.googlers  
  2.   
  3. import javax.microedition.khronos.egl.EGLConfig;  
  4. import javax.microedition.khronos.opengles.GL10;  
  5. import android.app.Activity;  
  6. import android.opengl.GLSurfaceView;  
  7. import android.os.Bundle;  
  8.    
  9.   
  10. public class ClearActivity extends Activity {  
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         mGLView = new GLSurfaceView(this);  
  15.         mGLView.setRenderer(new ClearRenderer());  
  16.         setContentView(mGLView);  
  17.     }  
  18.   
  19.     @Override  
  20.     protected void onPause() {  
  21.         super.onPause();  
  22.         mGLView.onPause();  
  23.     }  
  24.   
  25.     @Override  
  26.   
  27.     protected void onResume() {  
  28.         super.onResume();  
  29.         mGLView.onResume();  
  30.     }  
  31.   
  32.     private GLSurfaceView mGLView;  
  33. }  
  34.   
  35. class ClearRenderer implements GLSurfaceView.Renderer {  
  36.   
  37.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  38.         // Do nothing special.  
  39.     }   
  40.   
  41.     public void onSurfaceChanged(GL10 gl, int w, int h) {  
  42.         gl.glViewport(00, w, h);  
  43.     }  
  44.   
  45.     public void onDrawFrame(GL10 gl) {  
  46.         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);  
  47.     }  
  48. }  
 
这个程序并没有做太多东西:它在每帧是清除屏幕到黑色。但是它是一个完整的OpenGL应用程序,正确地按照Activity(活动)的生命周期实现。当活动暂停渲染它也暂停渲染,活动恢复它也恢复。你可以把这个例子作为一个基本的交互的示例程序。仅仅更多地调用了ClearRenderer.onDrawFrame() 方法。注意你甚至不需要子类化一个GLSurfaceView视图。
 
GLSurfaceView.Renderer 有三个方法:
onSurfaceCreated() :在开始渲染的时候被调用,无论什么时候OpenGL ES 渲染不得不重新被创建。(渲染是典型的丢失并重新创建当活动被暂停或恢复。)该方法一个创建长生命周期OpenGL资源(如材质)的好地方。
onSurfaceChanged():该方法在surface大小改变时被调用。这是设置你opengl视图端的好地方。如果相机是固定的,不会围着场景移动,你也可以在这里设置你的相机。
onDrawFrame():每帧的时候该方法都会被调用,这个用于画场景是可靠的。你完全可以通过调用glClear方法开清楚帧缓存,接着通过其他的opengl ES来调用画当前的场景。
 
用户如何输入?
假如你想做一个可以交互的程序(如游戏),通常你会实现GLSurfaceView子类,因为这是很容易获取用户输入事件。以下代码是一个清晰的长例子展示给你怎样做到这个:
 
[java] view plain copy
  1. package com.javaeye.googlers;  
  2.   
  3. import javax.microedition.khronos.egl.EGLConfig;  
  4. import javax.microedition.khronos.opengles.GL10;  
  5. import android.app.Activity;  
  6. import android.content.Context;  
  7. import android.opengl.GLSurfaceView;  
  8. import android.os.Bundle;  
  9. import android.view.MotionEvent;  
  10.   
  11. public class ClearActivity extends Activity {  
  12.   
  13.     @Override  
  14.     protected void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         mGLView = new ClearGLSurfaceView(this);  
  17.         setContentView(mGLView);  
  18.     }  
  19.   
  20.     @Override  
  21.     protected void onPause() {  
  22.         super.onPause();  
  23.         mGLView.onPause();  
  24.     }  
  25.   
  26.     @Override  
  27.     protected void onResume() {  
  28.         super.onResume();  
  29.         mGLView.onResume();  
  30.     }  
  31.   
  32.     private GLSurfaceView mGLView;  
  33. }  
  34.   
  35. class ClearGLSurfaceView extends GLSurfaceView {  
  36.   
  37.     public ClearGLSurfaceView(Context context) {  
  38.         super(context);  
  39.         mRenderer = new ClearRenderer();  
  40.         setRenderer(mRenderer);  
  41.     }  
  42.   
  43.     public boolean onTouchEvent(final MotionEvent event) {  
  44.         queueEvent(new Runnable(){  
  45.             public void run() {  
  46.                 mRenderer.setColor(event.getX() / getWidth(),  
  47.                         event.getY() / getHeight(), 1.0f);  
  48.             }});  
  49.   
  50.             return true;  
  51.         }  
  52.   
  53.         ClearRenderer mRenderer;  
  54. }  
  55.    
  56.   
  57. class ClearRenderer implements GLSurfaceView.Renderer {  
  58.   
  59.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  60.         // Do nothing special.  
  61.     }  
  62.   
  63.     public void onSurfaceChanged(GL10 gl, int w, int h) {  
  64.         gl.glViewport(00, w, h);  
  65.     }  
  66.   
  67.     public void onDrawFrame(GL10 gl) {  
  68.         gl.glClearColor(mRed, mGreen, mBlue, 1.0f);  
  69.         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);  
  70.     }  
  71.   
  72.    
  73.     public void setColor(float r, float g, float b) {  
  74.         mRed = r;  
  75.         mGreen = g;  
  76.         mBlue = b;  
  77.     }  
  78.   
  79.     private float mRed;  
  80.     private float mGreen;  
  81.     private float mBlue;  
  82. }  

 
这个应用每帧都在清楚屏幕。当你点击屏幕时,它清除颜色基于你触屏时间的X、Y坐标。注意在 ClearGLSurfaceView.onTouchEvent()中使用queueEvent()。queueEvent()方法被安全地用于在UI线程和渲染线程之间进行交流。如果你愿意,你还可以使用一些其他的java线程间交流技术,例如Renderer 类本身的同步方法。然而,queueing 事件经常是一种用于处理线程间信息交流的更简单方式。
 
其他的GLSurfaceView示例:
如果你厌烦了上面的示例,你还可以从android的ApiDemo中找到更经典的示例,所有的openGL ES示例都是用GLSurfaceView视图转变的:
 
GLSurfaceView - 一个旋转的三角形
Kube - 一个魔方例子
Translucent GLSurfaceView - 展示在一个透明的背景上显示3d动画
Textured Triangle - 显示一个带纹理的3D三角形
Sprite Text - 展示怎样用材质画出文字并混合进一个3d的场景中
Touch Rotate - 展示怎样旋转一个3D物体来响应用户的输入


0 0