一看就懂的 OpenGL 基礎(chǔ)概念(2):EGL爽冕,OpenGL 與設(shè)備的橋梁丨音視頻基礎(chǔ)

這個(gè)公眾號(hào)會(huì)路線圖 式的遍歷分享音視頻技術(shù)音視頻基礎(chǔ)音視頻工具音視頻工程示例音視頻工業(yè)實(shí)戰(zhàn)

通過(guò)《一看就懂的 OpenGL 基礎(chǔ)概念》一文垦藏,我們介紹了 OpenGL 的角色梆暖、渲染架構(gòu)、狀態(tài)機(jī)掂骏、渲染管線等內(nèi)容轰驳,我們接著來(lái)看看它如何在設(shè)備上實(shí)現(xiàn)渲染。

6芭挽、EGL

我們這里只討論 iOS/Android 設(shè)備滑废,所以這里的 OpenGL 也對(duì)應(yīng)的是 OpenGL ES。

如果我們了解了 OpenGL ES 就會(huì)知道袜爪,雖然它定義了一套移動(dòng)設(shè)備的圖像渲染 API蠕趁,但是并沒(méi)有定義窗口系統(tǒng)。為了讓 GLES 能夠適配各種平臺(tái)辛馆,GLES 需要與知道如何通過(guò)操作系統(tǒng)創(chuàng)建和訪問(wèn)窗口的庫(kù)結(jié)合使用俺陋,這就有了 EGL,EGL 是 OpenGL ES 渲染 API 和本地窗口系統(tǒng)之間的一個(gè)中間接口層昙篙,它主要由系統(tǒng)制造商實(shí)現(xiàn)腊状。EGL 提供如下機(jī)制:

  • 與設(shè)備的原生窗口系統(tǒng)通信;
  • 查詢繪圖圖層的可用類型和配置苔可;
  • 創(chuàng)建繪圖圖層缴挖;
  • 在 OpenGL ES 和其他圖形渲染 API 之間同步渲染;
  • 管理紋理貼圖等渲染資源焚辅。

EGL 是 OpenGL ES 與設(shè)備的橋梁映屋,以實(shí)現(xiàn)讓 OpenGL ES 能夠在當(dāng)前設(shè)備上進(jìn)行繪制。

EGL 架構(gòu)

EGL 架構(gòu)

6.1同蜻、Android EGL

Android 平臺(tái)自 2.0 版本之后圖形系統(tǒng)的底層渲染均由 OpenGL ES 負(fù)責(zé)棚点,其 EGL 架構(gòu)實(shí)現(xiàn)如下圖所示:

Android EGL 架構(gòu)

Android EGL 架構(gòu)

  • Display 是對(duì)實(shí)際顯示設(shè)備的抽象。在 Android 上的實(shí)現(xiàn)類是 EGLDisplay湾蔓。
  • Surface 是對(duì)用來(lái)存儲(chǔ)圖像的內(nèi)存區(qū)域 FrameBuffer 的抽象瘫析,包括 Color Buffer、Stencil Buffer默责、Depth Buffer贬循。在 Android 上的實(shí)現(xiàn)類是 EGLSurface
  • Context 存儲(chǔ) OpenGL ES 繪圖的一些狀態(tài)信息桃序。在 Android 上的實(shí)現(xiàn)類是 EGLContext甘有。

本地窗口相關(guān)的 API 提供了訪問(wèn)本地窗口系統(tǒng)的接口,而 EGL 可以創(chuàng)建渲染表面 EGLSurface 葡缰,同時(shí)提供了圖形渲染上下文 EGLContext亏掀,用來(lái)進(jìn)行狀態(tài)管理忱反,接下來(lái) OpenGL ES 就可以在這個(gè)渲染表面上繪制。

使用 EGL 在平臺(tái)實(shí)現(xiàn)渲染步驟大致如下:

  • 1)調(diào)用 eglGetDisplay 來(lái)獲得 EGLDisplay 對(duì)象滤愕,從而建立與平臺(tái)窗口系統(tǒng)的聯(lián)系温算,這個(gè) EGLDisplay 將作為 OpenGL ES 的渲染目標(biāo);
  • 2)調(diào)用 eglInitialize 初始化 EGL间影;
  • 3)調(diào)用 eglChooseConfig 來(lái)獲得 EGLConfig 對(duì)象注竿,從而確定渲染表面的配置信息
  • 4)通過(guò) EGLDisplay 和 EGLConfig 對(duì)象魂贬,調(diào)用 eglCreateWindowSurfaceeglCreatePbufferSurface 方法得到 EGLSurface巩割,從而創(chuàng)建渲染表面,其中 eglCreateWindowSurface 用于創(chuàng)建屏幕上渲染區(qū)域付燥,eglCreatePbufferSurface 用于創(chuàng)建離屏渲染區(qū)域宣谈;
  • 5)通過(guò) EGLDisplay 和 EGLConfig,調(diào)用 eglCreateContext 獲得 EGLContext 對(duì)象键科,從而創(chuàng)建渲染上下文闻丑,OpenGL 的任何一條指令都是必須在自己的 OpenGL 上下文環(huán)境中執(zhí)行;
  • 6)調(diào)用 eglMakeCurrent 將 EGLSurface勋颖、EGLContext嗦嗡、EGLDisplay 三者綁定就完成了上下文綁定,綁定成功之后 OpenGL ES 的環(huán)境就創(chuàng)建好了饭玲,接下來(lái)就可以開(kāi)始渲染了侥祭;

通過(guò)上面的步驟就做好了 EGL 的準(zhǔn)備工作:一方面為 OpenGL ES 渲染提供了目標(biāo) EGLDisplay 及上下文環(huán)境 EGLContext,可以接收到 OpenGl ES 渲染出來(lái)的紋理茄厘;另一方面我們連接好了設(shè)備顯示屏 EGLSurface(這里可能是 SurfaceView 或者 TextureView)矮冬。接下來(lái),由于 OpenGL ES 的渲染必須新開(kāi)一個(gè)線程蚕断,并為該線程綁定顯示設(shè)備及上下文環(huán)境(EGLContext),所以 eglMakeCurrent() 就是來(lái)綁定該線程的顯示設(shè)備及上下文的入挣。

  • 7)OpenGL ES 完成繪制后亿乳,調(diào)用 eglSwapBuffers 方法交換前后緩沖,將繪制內(nèi)容顯示到屏幕上径筏,而離屏渲染不需要調(diào)用此方法葛假;

這里需要注意的是 EGL 的工作模式是雙緩沖模式,其內(nèi)部有兩個(gè) FrameBuffer(幀緩沖區(qū)):BackFrameBuffer 和 FrontFrameBuffer滋恬,當(dāng) EGL 將一個(gè) FrameBuffer 顯示到屏幕上的時(shí)候聊训,另一個(gè) FrameBuffer 就在后臺(tái)等待 OpenGL ES 進(jìn)行渲染輸出。

直到調(diào)用了 eglSwapBuffer() 這條指令的時(shí)候恢氯,才會(huì)把前臺(tái)的 FrameBuffer 和后臺(tái)的 FrameBuffer 進(jìn)行交換带斑,這時(shí)界面呈現(xiàn)的就是 OpenGL ES 剛剛渲染的內(nèi)容了鼓寺。

這樣做的原因是如果應(yīng)用程序使用單緩沖繪圖時(shí)可能會(huì)存在圖像閃爍的問(wèn)題,因?yàn)閳D像生成不是一下子被繪制出來(lái)的勋磕,而是按照從左到右妈候、從上到下逐像素繪制的。如果最終圖像不是在瞬間全部展示給用戶挂滓,而是通過(guò)把繪制過(guò)程也展示出來(lái)了苦银,這會(huì)導(dǎo)致用戶看到的渲染效果出現(xiàn)閃爍。為了規(guī)避這個(gè)問(wèn)題赶站,可以使用雙緩沖渲染:前緩沖保存著最終輸出的圖像幔虏,它會(huì)在屏幕上顯示;而所有的的渲染指令都會(huì)在后緩沖上繪制贝椿,對(duì)用戶屏蔽從左到右想括、從上到下逐像素繪制的過(guò)程,這樣就可以避免閃爍了团秽。

  • 8)繪制結(jié)束后主胧,不再需要使用 EGL 時(shí),需要調(diào)用 eglMakeCurrent 取消綁定习勤,調(diào)用 eglDestroyContext踪栋、eglDestroySurfaceeglTerminate 等函數(shù)銷毀 EGLDisplay图毕、EGLSurface夷都、EGLContext 三個(gè)對(duì)象。

《RenderDemo(1):用 OpenGL 畫(huà)一個(gè)三角形》 Android Demo 的 KFGLContext 類中就可以看到上面這套流程予颤。

不過(guò)囤官,如果你覺(jué)得上述配置 EGL 的流程太麻煩的話,Android 平臺(tái)提供了 GLSurfaceView類實(shí)現(xiàn)了 Display蛤虐、Surface党饮、Context 的管理,即 GLSurfaceView 內(nèi)部實(shí)現(xiàn)了對(duì) EGL 的封裝驳庭,可以很方便地利用接口 GLSurfaceView.Renderer 的實(shí)現(xiàn)刑顺,使用 OpenGL ES API 進(jìn)行渲染繪制。GLSurfaceView 提升了 OpenGL ES 開(kāi)發(fā)的便利性饲常,當(dāng)然也相應(yīng)的失去了一些靈活性蹲堂。

參考:

  • EGL 作用及其使用[1]
  • EGL[2]

6.2、iOS EAGL

iOS 平臺(tái)對(duì) EGL 的實(shí)現(xiàn)是 EAGL(Embedded Apple Graphics Library) 贝淤。OpenGL ES 系統(tǒng)與本地窗口(UIKit)系統(tǒng)的橋接由 EAGL 上下文系統(tǒng)實(shí)現(xiàn)柒竞。

與 Android EGL 不同的是,iOS EAGL 不會(huì)讓?xiě)?yīng)用直接向 BackFrameBuffer 和 FrontFrameBuffer 進(jìn)行繪制播聪,也不會(huì)讓?xiě)?yīng)用直接控制雙緩沖區(qū)的交換(swap)朽基,系統(tǒng)自己保留了這些操作權(quán)布隔,以便可以隨時(shí)使用 Core Animation 合成器來(lái)控制顯示的最終外觀。

Core Animation 是 iOS 上圖形渲染和動(dòng)畫(huà)的核心基礎(chǔ)架構(gòu)踩晶。 可以使用托管多種 iOS 系統(tǒng)內(nèi)容的圖層(UIKit执泰、Quartz 2D、OpenGL ES)渡蜻,來(lái)合成應(yīng)用的用戶界面或者其他視覺(jué)顯示术吝。

OpenGL ES 通過(guò) CAEAGLLayer 與 Core Animation 連接,CAEAGLLayer 是一種特殊類型的 Core Animation 圖層茸苇,它的內(nèi)容來(lái)自 OpenGL ES 的 RenderBuffer排苍,Core Animation 將 RenderBuffer 的內(nèi)容與其他圖層合成,并在屏幕上顯示生成的圖像学密。所以同一時(shí)刻可以有任意數(shù)量的層淘衙。Core Animation 合成器會(huì)聯(lián)合這些層并在后幀緩存中產(chǎn)生最終的像素顏色,然后切換緩存腻暮。

Core Animation 與 OpenGL ES 共享 RenderBuffer

Core Animation 與 OpenGL ES 共享 RenderBuffer

一個(gè)應(yīng)用提供的圖層與操作系統(tǒng)提供的圖層混合起來(lái)可以產(chǎn)生最終的顯示外觀彤守。如下圖所示,OpenGL ES 圖層顯示了一個(gè)應(yīng)用生成的旋轉(zhuǎn)立方體哭靖,但是在顯示器頂部的顯示狀態(tài)欄圖層則是由操作系統(tǒng)生成和控制的具垫,此圖顯示的是合并兩個(gè)圖層來(lái)產(chǎn)生后幀緩存中的顏色數(shù)據(jù)的過(guò)程,交換后试幽,我們看到的就是前幀緩存上的內(nèi)容筝蚕。

iOS 多圖層合成

iOS 多圖層合成

所以,iOS 的 EAGL 配置過(guò)程其實(shí)就是使用 CoreAnimation 的 layer 來(lái)支持 OpenGL ES 渲染的過(guò)程铺坞,步驟大致如下:

  • 1)創(chuàng)建一個(gè) EAGL 圖層 CAEAGLLayer 對(duì)象起宽,并設(shè)置好它的屬性;
  • 2)創(chuàng)建 OpenGL ES 上下文 EAGLContext济榨,并設(shè)置為當(dāng)前上下文環(huán)境坯沪;
  • 3)創(chuàng)建一個(gè)顏色渲染緩沖區(qū)對(duì)象 ColorRenderBuffer,并調(diào)用 renderbufferStorage:fromDrawable: 為其分配存儲(chǔ)空間擒滑,這里其實(shí)是將 CAEAGLLayer 的繪制存儲(chǔ)區(qū)共享為了 ColorRenderBuffer 的繪制緩沖區(qū)腐晾。分配緩沖區(qū)需要的寬、高橘忱、像素格式等信息都會(huì)從 layer 中取得赴魁;

需要注意的是卸奉,如果 CAEAGLLayer 的 bounds 或其他屬性變了钝诚,需要重新分配 ColorRenderBuffer 的存儲(chǔ)空間,否則會(huì)出現(xiàn) ColorRenderBuffer 和 CAEAGLLayer 的尺寸不匹配榄棵。

  • 4)創(chuàng)建幀緩沖區(qū) FrameBuffer 對(duì)象凝颇,并將 ColorRenderBuffer 綁定為它的附件潘拱;
  • 5)從顏色渲染緩沖區(qū) ColorRenderBuffer 獲取寬高信息;
  • 6)根據(jù)需要?jiǎng)?chuàng)建一個(gè)深度渲染緩沖區(qū) DepthRenderBuffer 對(duì)象拧略,并綁定為 FrameBuffer 的附件芦岂;
  • 7)根據(jù)需要檢測(cè) FrameBuffer 的狀態(tài);
  • 8)將 CAEAGLLayer 添加到 Core Animation 的圖層樹(shù)中垫蛆;
  • 9)在繪制動(dòng)作完成后禽最,調(diào)用 EAGLContext 的 presentRenderbuffer: 方法,就可以將繪制結(jié)果顯示在屏幕上了袱饭。

《RenderDemo(1):用 OpenGL 畫(huà)一個(gè)三角形》 iOS Demo 的 DMTriangleRenderView 類中可以看到類似的流程川无,只不過(guò) Demo 中我們是創(chuàng)建了一個(gè) UIView 的子類,并重寫(xiě)它的 +layerClass 方法返回 CAEAGLLayer 類型來(lái)獲得了一個(gè) CAEAGLLayer 對(duì)象用于 OpenGL ES 渲染虑乖。

同樣的懦趋,如果你覺(jué)得上述流程太麻煩,iOS 平臺(tái)還提供了封裝好的 GLKView 來(lái)簡(jiǎn)化我們使用 OpenGL ES疹味,GLKView 是對(duì) CAEAGLLayer 的封裝仅叫,內(nèi)嵌了配置 Core Animation 支持 OpenGL ES 的流程。

參考:

  • iOS OpenGL ES 應(yīng)用開(kāi)發(fā)實(shí)踐指南[3]
  • iOS OpenGL ES Programming Guide[4]
  • OpenGL ES 在 iOS 中的上下文環(huán)境搭建[5]

參考資料

[1]

EGL 作用及其使用: https://blog.csdn.net/u010281924/article/details/105296617

[2]

EGL: https://blog.csdn.net/Kennethdroid/article/details/99655635

[3]

iOS OpenGL ES 應(yīng)用開(kāi)發(fā)實(shí)踐指南: https://book.douban.com/subject/24849591/

[4]

iOS OpenGL ES Programming Guide: https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html

[5]

OpenGL ES 在 iOS 中的上下文環(huán)境搭建: http://www.reibang.com/p/c34c14589e0c

- 完 -

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末糙捺,一起剝皮案震驚了整個(gè)濱河市诫咱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌继找,老刑警劉巖遂跟,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異婴渡,居然都是意外死亡幻锁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門边臼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)哄尔,“玉大人,你說(shuō)我怎么就攤上這事柠并×虢樱” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵臼予,是天一觀的道長(zhǎng)鸣戴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)粘拾,這世上最難降的妖魔是什么窄锅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮缰雇,結(jié)果婚禮上入偷,老公的妹妹穿的比我還像新娘追驴。我一直安慰自己,他們只是感情好疏之,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布殿雪。 她就那樣靜靜地躺著,像睡著了一般锋爪。 火紅的嫁衣襯著肌膚如雪丙曙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天其骄,我揣著相機(jī)與錄音河泳,去河邊找鬼。 笑死年栓,一個(gè)胖子當(dāng)著我的面吹牛拆挥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播某抓,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼纸兔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了否副?” 一聲冷哼從身側(cè)響起汉矿,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎备禀,沒(méi)想到半個(gè)月后洲拇,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曲尸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年赋续,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片另患。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纽乱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昆箕,到底是詐尸還是另有隱情鸦列,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布鹏倘,位于F島的核電站薯嗤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纤泵。R本人自食惡果不足惜骆姐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诲锹,春花似錦、人聲如沸涉馅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)稚矿。三九已至庸诱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晤揣,已是汗流浹背桥爽。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昧识,地道東北人钠四。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像跪楞,于是被迫代替她去往敵國(guó)和親缀去。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容