天天看點

Android OpenGL初探

最近在做一些Android OpenGL相關的工作,作為一名初學者,第一次接觸OpenGL難免有點畏懼心理,踩了很多坑。寫這篇文章介紹關于OpenGL的一些基礎知識,幫助大家能夠快速了解。

####OpenGL概念:

OpenGL(open Graphics Library):開放圖形庫,主要用于2D、3D矢量圖形的繪制。封裝了一些列API用于圖像繪制,OpenGL具有非常好的跨平台性,可在windows、linux、MacOS上使用,它依賴于硬體的支援,能夠通過GPU高效繪制各種圖形和動畫。

####Android OpenGL ES:

Android的OpenGL ES庫中封裝了大量API,對OpenGL繪制操作提供了非常好的支援。App開發者可以通過這些接口實作OpenGL的繪制,其實這些接口底層都是通過native方法調用OpenGL庫的API。應用開發者隻需要了解上層接口的含義、參數的意義,就可以完成簡單的繪制,對于開發者來說相當友好。

####EGL相關概念:

EGL是Android為OpenGL ES提供平台獨立性而設計的,上面我們提到過,OpenGL其實是通過GPU進行渲染。但是我們的程式是運作在CPU上,要與GPU關聯,就需要通過EGL,它相當于Android上層應用與GPU通訊的中間層。 要在Android平台實作OpenGL渲染,需要完成一系列的EGL操作,主要為下面幾步:

#####1、擷取顯示裝置(EGLDisplay)

擷取将要用于顯示的裝置,有些系統具有多個顯示器,會存在多個Display。在Android上通過調用EGL10的eglGetDisplay(Object native_display)方法獲得EGLDisplay對象,通常傳入的參數為EGL10.EGL_DEFAULT_DISPLAY。

#####2、初始化EGL

調用EGL10的eglInitialize(EGLDisplay display, int[] major_minor)方法完成初始化操作。display參數即為上一步擷取的對象,major_minor傳入的是一個int數組,通常傳入的是一個大小為2的數組。

#####3、選擇Config配置

調用EGL10的eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config)方法,參數1、2其意明顯,參數3用于存放輸出的configs,參數4指定最多輸出多少個config,參數5由EGL系統寫入,表明滿足attributes的config一共有多少個。

#####4、建立EGL環境(EGLContext)

eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list);參數1即為上面擷取的Display,參數2為上一步chooseConfig傳入的configs,share_context,是否有context共享,共享的contxt之間亦共享所有資料,通常設定為EGL_NO_CONTEXT代表不共享。attrib_list為int數組 {EGL_CONTEXT_CLIENT_VERSION, 2,EGL10.EGL_NONE };中間的2代表的是OpenGL ES的版本。

#####4、建立EGLSurface

eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);參數1、2均為上述步驟得到的結果,參數3為上層建立的用于繪制内容的surface對象,參數4常設定為null。

#####5、設定OpenGL的渲染環境

eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);該方法的參數意義很明确,該方法在異步線程中被調用,該線程也會被成為GL線程,一旦設定後,所有OpenGL渲染相關的操作都必須放在該線程中執行。下面介紹GLSurfaceView的流程會描述該線程。

通過上述5步操作,就完成了EGL的初始化設定,便可以進行OpenGL的渲染操作。

####GLSurfaceView繪制流程:

Android的UI繪制必須要在主線程,而OpenGL的某些操作比較耗時,放在主線程顯然是不合适的。是以,Android提供了GLSurfaceView用于OpenGL的繪制,下面我們将簡單介紹一下GLSurfaceView的繪制流程。

#####Renderer

public interface Renderer {
        /**
         * Called when the surface is created or recreated.
         * <p>
         * Called when the rendering thread
         * starts and whenever the EGL context is lost. The EGL context will typically
         * be lost when the Android device awakes after going to sleep.
         * <p>
         * Since this method is called at the beginning of rendering, as well as
         * every time the EGL context is lost, this method is a convenient place to put
         * code to create resources that need to be created when the rendering
         * starts, and that need to be recreated when the EGL context is lost.
         * Textures are an example of a resource that you might want to create
         * here.
         * <p>
         * Note that when the EGL context is lost, all OpenGL resources associated
         * with that context will be automatically deleted. You do not need to call
         * the corresponding "glDelete" methods such as glDeleteTextures to
         * manually delete these lost resources.
         * <p>
         * @param gl the GL interface. Use <code>instanceof</code> to
         * test if the interface supports GL11 or higher interfaces.
         * @param config the EGLConfig of the created surface. Can be used
         * to create matching pbuffers.
         */
        void onSurfaceCreated(GL10 gl, EGLConfig config);

        /**
         * Called when the surface changed size.
         * <p>
         * Called after the surface is created and whenever
         * the OpenGL ES surface size changes.
         * <p>
         * Typically you will set your viewport here. If your camera
         * is fixed then you could also set your projection matrix here:
         * <pre class="prettyprint">
         * void onSurfaceChanged(GL10 gl, int width, int height) {
         *     gl.glViewport(0, 0, width, height);
         *     // for a fixed camera, set the projection too
         *     float ratio = (float) width / height;
         *     gl.glMatrixMode(GL10.GL_PROJECTION);
         *     gl.glLoadIdentity();
         *     gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
         * }
         * </pre>
         * @param gl the GL interface. Use <code>instanceof</code> to
         * test if the interface supports GL11 or higher interfaces.
         * @param width
         * @param height
         */
        void onSurfaceChanged(GL10 gl, int width, int height);

        /**
         * Called to draw the current frame.
         * <p>
         * This method is responsible for drawing the current frame.
         * <p>
         * The implementation of this method typically looks like this:
         * <pre class="prettyprint">
         * void onDrawFrame(GL10 gl) {
         *     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
         *     //... other gl calls to render the scene ...
         * }
         * </pre>
         * @param gl the GL interface. Use <code>instanceof</code> to
         * test if the interface supports GL11 or higher interfaces.
         */
        void onDrawFrame(GL10 gl);
    }
           

使用GLSurfaceView時需要自定義一個渲染器,實作Render接口,并将渲染器對象設定給GLSurfaceView。Render接口中定義的方法意義非常明确。将GLSurfaceView放入布局中,自定義實作Render接口就能完成OpenGL的繪制了,使用上非常簡單。

#####GLThread

GLThread就是上面在介紹EGL時提到的GL線程,所有的EGL相關操作都在該線程中被調用,OpenGL繪制相關操作都由該線程完成,包括Render中的方法。而該線程最主要的方法就是guardedRun(),所有的操作都在該方法中執行。該方法的代碼較長,在此處不貼出。不過邏輯還是比較好了解:

1、檢測渲染環境是否準備好(EGL的初始化操作);

2、循環調用Render的onDrawFrame方法;

3、 調用EGL10的eglSwapBuffers(EGLDisplay display, EGLSurface surface)方法,該方法才是真正的将surface中的内容渲染到螢幕的操作。

####結束語

本篇文章主要是為了介紹Android中使用OpenGL繪圖的一些基礎知識,沒有具體的實踐代碼。關于GLSurfaceView建議大家閱讀一下源碼,源碼比較容易了解。隻要了解了上述的過程,其實還是比較容易看懂的。後面的文章會通過執行個體介紹OpenGL的繪制。

繼續閱讀