最近在做一些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的繪制。