OpenGL ES for Android

1,102 阅读4分钟


简介

OpenGL是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API),而在嵌入式和移动平台的版本是OpenGL ES。Android最初就支持OpenGL ES的1.0版本,到现在已经支持到最新的3.2版本,下面的支持变化图 

当然这个版本支持不是绝对的,还有看硬件是否支持,例如genymotion模拟器只有OpenGL ES 2.0版本,如果你使用了高版本的API会导致崩溃。OpenGL ES 1.x版本和2.0版本语法差异巨大,3.x版本向前兼容2.0版本。因此我们学习的版本是2.0既可兼容近100%的设备。 版本支持声明 可以在AndroidManifest.xml中加入下面这行使用特性的声明,Google Play将会过滤掉不支持指定OpenGL ES版本的用户,拒绝他们安装。


版本支持声明


可以在AndroidManifet.xml中加入下面这行使用特性的声明,Google Play将会过滤掉不支持指定OpenGL ES版本的用户,拒绝他们安装。

  <!-- 需要OpenGL ES 2.0 -->
  <uses-feature android:glEsVersion="0x00020000" android:required="true" />

版本号的高16位表示主要版本,低16位表示次版本;那么如果需要3.0,3.1则可以如下配置

  <!-- 需要OpenGL ES 3.0 -->
  <uses-feature android:glEsVersion="0x00030000" android:required="true" />
  <!-- 需要OpenGL ES 3.1 -->
  <uses-feature android:glEsVersion="0x00030001" android:required="true" />

也可以在代码中判断gles的版本,version同样传入版本号即可(例如0x20000)

    public static boolean checkOpenGL(Activity activity, int version) {
        ActivityManager am = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
        if (am != null) {
            return am.getDeviceConfigurationInfo().reqGlEsVersion >= version;
        }
        return false;
    }


基础  


两个基本的类GLSurfaceView和GLSurfaceView.Renderer。
 GLSurfaceView:继承自SurfaceView,用来显示渲染的图像。如果想操作你的图像,需要扩展触摸监听事件来处理。 
GLSurfaceView.Renderer:GLSurfaceView的内部接口类,主要负责渲染图像。

GLSurfaceView主要方法: 

  • setEGLContextClientVersion:设置OpenGL ES的版本,只能设置主要版本(例如:1,2,3),不能设置次要版本。
  •  setEGLContextFactory:设置OpenGL ES的版本构建器,默认的构建器是根据版本设置的,可以自定义成版本自适应。
  •  setRenderer:设置Renderer,如果不设置,那么界面显示的就是一片空白。
  •  setRenderMode:设置Renderer的模式,有这么两种:1、RENDERMODE_WHEN_DIRTY(仅在创建时或调用requestRender时才会渲染内容);2、RENDERMODE_CONTINUOUSLY(不停的渲染)。 
  • onPause:暂停渲染,在页面不再显示时可以调用,减少性能开销,例如在Activity的onStop时。 
  • onResume:恢复渲染,类似onPause。 requestRender:请求渲染,通常是RENDERMODE_WHEN_DIRTY模式时使用,必须在setRenderer后才能使用。
  •  queueEvent:插入一个Runnable任务到后台渲染线程上执行,必须在setRenderer后才能使用。
  •  setDebugFlags:设置debug模式,主要有两种:1、DEBUG_CHECK_GL_ERROR(当GL调用glError()方法后如果发生异常会打印。主要用来跟踪OpenGL错误。);2、DEBUG_LOG_GL_CALLS(打印所有GL的verbose级别的日志); 

GLSurfaceView.Renderer的主要方法: 

  • onSurfaceCreated(GL10 gl, EGLConfig config):当Surface创建或重新创建时,这时可以进行初始化。
  •  onSurfaceChanged(GL10 gl, int width, int height):Surface尺寸改变时,返回当前surface宽高,可以进行下一步操作。 
  • onDrawFrame(GL10 gl):渲染绘制当前一帧时会调用。
 OpenGL类:在包android.opengl下,主要有GLES20(OpenGL ES 2.0版本),GLES30,GLES31,GLES32和。 现在我们创建一个自定义的SurfaceView 

  public class BaseGLView extends GLSurfaceView {
      public BaseGLView(Context context) {
          this(context, null);
      }
 
      public BaseGLView(Context context, AttributeSet attrs) {
          super(context, attrs);
          setDebug();
          init();
      }
 
      protected void setDebug() {
          setDebugFlags(BuildConfig.DEBUG ? DEBUG_LOG_GL_CALLS : DEBUG_CHECK_GL_ERROR);
      }
 
      protected void init() {
          // 设置版本
          setEGLContextClientVersion(2);
          // 设置Renderer
          setRenderer(new BaseRenderer());
          // 设置渲染模式(默认RENDERMODE_CONTINUOUSLY)
          setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
      }
  }


新建一个自定义的Renderer类


  public class BaseRenderer implements GLSurfaceView.Renderer {
      private int bg = Color.BLACK;
 
      public BaseRenderer() {
      }
 
      public BaseRenderer(int bg) {
          this.bg = bg;
      }
 
 
      @Override
      public void onSurfaceCreated(GL10 gl, EGLConfig config) {
          // 设置背景色
          GLES20.glClearColor(Color.red(bg) / 255.0f, Color.green(bg) / 255.0f,
                  Color.blue(bg) / 255.0f, Color.alpha(bg) / 255.0f);
      }
 
      @Override
      public void onSurfaceChanged(GL10 gl, int width, int height) {
          // 设置显示范围
          GLES20.glViewport(0, 0, width, height);
      }
 
      @Override
      public void onDrawFrame(GL10 gl) {
          // 清屏
          GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
      }
  }


这时在Activity中使用setContentView(new BaseGLView(this));就可以了,Activity显示的就是一个黑色背景的布局。源码可以参考代码:

(https://github.com/jklwan/OpenGLProject/blob/master/sample/src/main/java/com/chends/opengl/view/BaseGLView.java) 

主要参考资料: Android官方文档:

(https://developer.android.com/guide/topics/graphics/opengl) OpenGL ES文档(https://www.khronos.org/opengles/) learnopengl(https://learnopengl-cn.github.io/) 《OpenGL ES 2 for Android: A Quick-Start Guide》(英文原版电子书)

Android的文档主要用来入门,参考learnopengl的文档来进行一步步的学习,当然learnopengl是桌面版的文档,和移动版有些区别,需要OpenGL ES文档和其他来学习。

最后是一个自定义的EGLContextFactory,当3.0可用时使用3.0,当3.0版本不可用时使用2.0 

  private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
 
          private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
          private BaseListener<Integer> listener;
 
          public ContextFactory(BaseListener<Integer> listener) {
              this.listener = listener;
          }
 
          @Override
          public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
              EGLContext context = null;
              Integer version = null;
              try {
                  context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT,
                          new int[]{EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE});
              } catch (Exception ex) {
                  LogUtil.e(ex);
              }
 
              if (context == null || context == EGL10.EGL_NO_CONTEXT) {
                  LogUtil.d("un support OpenGL ES 3.0 ");
                  try {
                      context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT,
                              new int[]{EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE});
                  } catch (Exception ex) {
                      LogUtil.e(ex);
                  }
              } else {
                  version = 3;
              }
              if (context == null || context == EGL10.EGL_NO_CONTEXT) {
                 LogUtil.d("un support OpenGL ES 2.0 ");
              } else {
                  version = 2;
              }
              if (listener != null) {
                  listener.onFinish(version);
              }
              return context;
          }
 
          @Override
          public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
              if (!egl.eglDestroyContext(display, context)) {
                  LogUtil.d("destroyContext false");
              }
          }
      }