OpenGL 是跨平台的、專業的圖形編程接口,而接口的實現是由廠商來完成的。
而當我們使用這組接口完成繪製之後,要把結果顯示在屏幕上,就要用到 EGL 來完成這個轉換工作。
EGL 是 OpenGL ES 渲染 API 和本地窗口系統(native platform window system)之間的一個中間接口層,它也主要由廠商來實現。EGL 提供了如下機制:
與設備的原生窗口系統通信
查詢繪圖表面的可用類型和配置
創建繪圖表面
在 OpenGL ES 和其他圖形渲染 API 之間同步渲染
管理紋理貼圖等渲染資源
為了讓 OpenGL ES 能夠繪製在當前設備上,我們需要 EGL 作為 OpenGL ES 與設備的橋樑。
我們可以直接用 GLSurfaceView 來進行 OpenGL 的渲染,就是因為在 GLSurfaceView 的內部已經完成了對 EGL 的使用封裝,當然我們也可以封裝自己的 EGL 環境。
EGL 使用實踐EGL 的使用要遵循一些固定的步驟,按照這些步驟去配置、創建、渲染、釋放。
創建與本地窗口系統的連接
調用 eglGetDisplay 方法得到 EGLDisplay
初始化 EGL 方法
調用 eglInitialize 方法初始化
確定渲染表面的配置信息
調用 eglChooseConfig 方法得到 EGLConfig
創建渲染上下文
通過 EGLDisplay 和 EGLConfig ,調用 eglCreateContext 方法創建渲染上下文,得到 EGLContext
創建渲染表面
通過 EGLDisplay 和 EGLConfig ,調用 eglCreateWindowSurface 方法創建渲染表面,得到 EGLSurface
綁定上下文
通過 eglMakeCurrent 方法將 EGLSurface、EGLContext、EGLDisplay 三者綁定,接下來就可以使用 OpenGL 進行繪製了。
交換緩衝
當用 OpenGL 繪製結束後,使用 eglSwapBuffers 方法交換前後緩衝,將繪製內容顯示到屏幕上
釋放 EGL 環境
繪製結束,不再需要使用 EGL 時,取消 eglMakeCurrent 的綁定,銷毀 EGLDisplay、EGLSurface、EGLContext。
如果對 EGLDisplay、EGLSurface 、EGLContext 這些抽象概念傻傻分不清楚,可以參考這幅圖:

其中:
Display(EGLDisplay) 是對實際顯示設備的抽象
Surface(EGLSurface)是對用來存儲圖像的內存區域
FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer
Context (EGLContext) 存儲 OpenGL ES繪圖的一些狀態信息
使用 EGL 的具體步驟如下:
創建與本地窗口系統的連接通過 eglGetDisplay 方法創建與本地窗口系統的連接,返回的是 EGLDisplay 類型對象,可以把它抽象理解成設備的顯示屏幕。
1privateEGLDisplaymEGLDisplay=EGL14.EGL_NO_DISPLAY;2//創建與本地窗口系統的連接3mEGLDisplay=EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);4//如果創建之後還是EGL_NO_DISPLAY,表示創建失敗5if(mEGLDisplay==EGL14.EGL_NO_DISPLAY){6//failed7}這一步就可以看成是選擇顯示設備,一般都是選擇默認的顯示設備,也就是手機屏幕。
初始化 EGL創建 EGLDisplay 對象之後,要將它進行初始化。
1//EGL的主版本號2privateint[]mMajorVersion=newint[1];3//EGL的次版本號4privateint[]mMinorVersion=newint[1];5booleanresult=EGL14.eglInitialize(mEGLDisplay,mMajorVersion,0,mMajorVersion,0);6if(!result){7//failed8}初始化的時候可以獲得 EGL 的主版本號與次版本號。
確定可用的渲染表面(Surface)配置把 Surface 看成是一個渲染表面,渲染表面包含一些配置信息,也就是 EGLConfig 屬性:
比如:
EGL_RED_SIZE:顏色緩衝區中紅色用幾位來表示
EGL_BLUE_SIZE:顏色緩衝區中藍色用幾位來表示
更多的 EGLConfig 屬性參考這裡:
https://www.jianshu.com/p/9db986365cda。
有兩種方式用來確定可用的渲染表面配置。
通過 eglGetConfigs 的方法獲取底層窗口系統支持的所有 EGL 渲染表面配置,再用 eglGetConfigAttrib 查詢每個 EGLConfig 的信息。
1publicstaticnativebooleaneglGetConfigs( 2EGLDisplaydpy, 3EGLConfig[]configs, 4intconfigsOffset, 5intconfig_size, 6int[]num_config, 7intnum_configOffset 8); 910publicstaticnativebooleaneglGetConfigAttrib(11EGLDisplaydpy,12EGLConfigconfig,13intattribute,14int[]value,15intoffset16);另一種是創建好渲染表面配置列表,通過 eglChooseConfig 的方法,找到符合要求的 EGLConfig 配置。
1publicstaticnativebooleaneglChooseConfig( 2EGLDisplaydpy, 3int[]attrib_list, 4intattrib_listOffset, 5EGLConfig[]configs, 6intconfigsOffset, 7intconfig_size, 8int[]num_config, 9intnum_configOffset10);第二種方法相對於第一種來說,不用再查詢每一個 EGLConfig 屬性了。
具體使用如下:
1//定義EGLConfig屬性配置 2privatestaticfinalint[]EGL_CONFIG={ 3EGL14.EGL_RED_SIZE,CFG_RED_SIZE, 4EGL14.EGL_GREEN_SIZE,CFG_GREEN_SIZE, 5EGL14.EGL_BLUE_SIZE,CFG_BLUE_SIZE, 6EGL14.EGL_ALPHA_SIZE,CFG_ALPHA_SIZE, 7EGL14.EGL_DEPTH_SIZE,CFG_DEPTH_SIZE, 8EGL14.EGL_STENCIL_SIZE,CFG_STENCIL_SIZE, 9EGL14.EGL_RENDERABLE_TYPE,EGL14.EGL_OPENGL_ES2_BIT,10EGL14.EGL_NONE,11};首先定義 EGLConfig 屬性配置數組,定義紅、綠、藍、透明度、深度、模板緩衝的位數,最後要以 EGL14.EGL_NONE 結尾。
1//所有符合配置的EGLConfig個數 2int[]numConfigs=newint[1]; 3//所有符合配置的EGLConfig 4EGLConfig[]configs=newEGLConfig[1]; 5 6//configs參數為null,會在numConfigs中輸出所有滿足EGL_CONFIG條件的config個數 7EGL14.eglChooseConfig(mEGLDisplay,EGL_CONFIG,0,null,0,0,numConfigs,0); 8//得到滿足條件的個數 9intnum=numConfigs[0];10if(num!=0){11//會獲取所有滿足EGL_CONFIG的config12EGL14.eglChooseConfig(mEGLDisplay,EGL_CONFIG,0,configs,0,configs.length,numConfigs,0);13//去第一個14mEGLConfig=configs[0];15}首先通過給 eglChooseConfig 相應參數設置為 null,找到符合條件的 EGLConfig 個數,如果不為空,則再一次調用,取第一個,就是想要的 EGLConfig 配置。
創建渲染上下文有了 EGLDisplay 和 EGLConfig 對象,就可以創建 渲染表面 EGLSurface和 渲染上下文 EGLContext。
在創建渲染上下文時,同樣要創建屬性信息,主要是指定 OpenGL 使用版本。
1privatestaticfinalint[]EGL_ATTRIBUTE={2EGL14.EGL_CONTEXT_CLIENT_VERSION,2,3EGL14.EGL_NONE,4};同樣,還是以 EGL14.EGL_NONE 結尾。
1privateEGLContextmEGLContext=EGL14.EGL_NO_CONTEXT;2//創建上下文3mEGLContext=EGL14.eglCreateContext(mEGLDisplay,mEGLConfig,EGL14.EGL_NO_CONTEXT,EGL_ATTRIBUTE,0);4if(mEGLContext==EGL14.EGL_NO_CONTEXT){5//failed6}創建渲染表面EGL 提供了兩種方式創建渲染表面,一種是可見的,渲染到屏幕上,一種是不可見的,也就離屏的。
eglCreatePbufferSurface:創建離屏的渲染表面
eglCreateWindowSurface:創建渲染到屏幕的渲染表面
無論使用哪種創建方式,也都需要創建配置信息。
1//創建渲染表面的配置信息2privatestaticfinalint[]EGL_SURFACE={3EGL14.EGL_NONE,4};5privateEGLSurfacemEGLSurface=EGL14.EGL_NO_SURFACE;6//surface參數是有SurfaceView.getHolder傳遞過來的7mEGLSurface=EGL14.eglCreateWindowSurface(mEGLDisplay,mEGLConfig,surface,EGL_SURFACE,0);對於渲染到屏幕上的創建,配置信息可以不添加什麼,但還是要以 EGL14.EGL_NONE 結尾。
而對於離屏的渲染表面創建,就還需要提供寬、高等信息,但是卻不需要提供 surface 的參數了。
1//使用eglCreatePbufferSurface就需要指定寬和高2int[]EGL_SURFACE={3EGL10.EGL_WIDTH,w,4EGL10.EGL_HEIGHT,h,5EGL10.EGL_NONE,6};7mEGLSurface=mEGL.eglCreatePbufferSurface(mEGLDisplay,mEGLConfig,EGL_SURFACE,0);綁定上下文當有了 EGLDisplay、EGLSurface、EGLContext 對象之後,就可以為 EGLContext 綁定上下文了。
1EGL14.eglMakeCurrent(mEGLDisplay,mEGLSurface,mEGLSurface,mEGLContext);當綁定上下文之後,就可以執行具體的繪製操作了,調用 OpenGL 相關的方法繪製圖形。
交換緩衝,進行顯示當繪製結束之後,就該把繪製結果顯示在屏幕上了。
1EGL14.eglSwapBuffers(mEGLDisplay,mEGLSurface);通過 eglSwapBuffers 方法,將後台繪製的緩衝顯示到前台。
釋放操作當繪製結束時,要進行相應的釋放操作。
1EGL14.eglMakeCurrent(mEGLDisplay,EGL14.EGL_NO_SURFACE,EGL14.EGL_NO_SURFACE,EGL14.EGL_NO_CONTEXT);2EGL14.eglDestroyContext(mEGLDisplay,mEGLContext);3EGL14.eglDestroySurface(mEGLDisplay,mEGLSurface);4EGL14.eglReleaseThread();5EGL14.eglTerminate(mEGLDisplay);67mEGLContext=EGL14.EGL_NO_CONTEXT;8mEGLDisplay=EGL14.EGL_NO_DISPLAY;9mEGLConfig=null;相當於就是把前面操作的 EGL 相關對象都釋放掉。
當完成這樣的一個流程之後,就基本上掌握了 EGL 的大部分使用。
EGL 操作的很多流程都是固定的,可以把它們再單獨做一層封裝,這裡可以參考 grafika 的封裝。
文章中具體代碼部分,可以參考我的 Github 項目,歡迎 Star 。
https://github.com/glumes/AndroidOpenGLTutorial
技術交流,歡迎加我微信:ezglumes ,拉你入技術交流群。
私信領取相關資料
推薦閱讀:
音視頻開發工作經驗分享 || 視頻版
OpenGL ES 學習資源分享
開通專輯 | 細數那些年寫過的技術文章專輯
NDK 學習進階免費視頻來了
你想要的音視頻開發資料庫來了
推薦幾個堪稱教科書級別的 Android 音視頻入門項目
覺得不錯,點個在看唄~
