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

android es opengl 代码包 安卓opengl es_ide

当然这个版本支持不是绝对的,还有看硬件是否支持,例如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版本的用户,拒绝他们安装。

<!-- 需要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;
    }

基础

两个基本的类GLSurfaceViewGLSurfaceView.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");
            }
        }
    }