學習OpenGL ES for Android(一)

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版本的用戶,拒絕他們安裝。

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章