1. khronos 簡介

khronos 是由 170 家企業(yè)組成的開源、非盈利的成員驅(qū)動型組織,主要負責(zé)開發(fā)、發(fā)布和維護免稅版權(quán)標(biāo)準,其范圍涉及到 3D圖像學(xué)、虛擬現(xiàn)實、增強現(xiàn)實、并行計算、視覺加速和機器學(xué)習(xí)。該組織制定的標(biāo)準及其相關(guān)的適應(yīng)性測試提高了軟件和中間軟件的創(chuàng)作,而且加速了動態(tài)多媒體的在跨平臺設(shè)備上的回放。
khronos 是 OpenGL ES 標(biāo)準的制定者、維護者,有什么疑問直接去官方找資料即可:
https://www.khronos.org/registry/OpenGL/specs
2. OpenGL 和 OpenGL ES
OpenGL ES 是 OpenGL 的子集,專門為嵌入式設(shè)備而設(shè)計,如手機端,另外 Play Station 也有使用;
嵌入式設(shè)備的因為諸多限制,比如能耗,所以 GPU 和 CPU 性能一般不如 PC 端,所以 OpenGL ES 的設(shè)計相對 OpenGL 就很精簡、高效,比較典型的區(qū)別如下:
- 在OpenGL ES的世界里,沒有四邊形、多邊形,無論多復(fù)雜的圖形都是由點、線和三角形組成的;
- 沒有g(shù)lBegin/glEnd/glVertex,只能用glDrawArrays/glDraw;
- 沒有double型數(shù)據(jù)類型,但加入了高性能的定點小數(shù)數(shù)據(jù)類型;
- 沒有實時將非壓縮圖片數(shù)據(jù)轉(zhuǎn)成壓縮貼圖(紋理)的功能,程序必須直接提供壓縮好的貼圖;
- 刪除了很多功能:顯示列表、求值器、索引色模式等等;
- glDrawArrays等函數(shù)中數(shù)據(jù)必須緊密排列,即間隔為 0;
7.各種數(shù)據(jù)的堆棧深度較低;
3. iOS 中 OpenGL ES 的版本
在學(xué)習(xí)時,一般參考 OpenGL 的官方教程:
https://learnopengl-cn.github.io/
但是需要注意的是,OpenGL 和 OpenGL ES 還是有很大的不同,比如 #version 的聲明,所以一些圖形學(xué)的概念可以參考 openGL 中的教程,但是涉及到具體的 Api 差異或者版本問題,最好直接看 OpenGL ES 的官方文檔;
這里是 openGL 和 openGL ES 相關(guān)文檔的歸檔:
https://www.khronos.org/registry/OpenGL/specs/es/

3. version
按照 LearnOpenGL 中的示例代碼,編譯著色器時如果使用到了#version 330 core,跑在 iOS 設(shè)備上,必定是會報錯的,報錯信息大概如下:
ERROR: 0:1: '' : version '330' is not supported
ERROR: 0:1: '' : syntax error: #version
那為什么會報錯呢?原因是因為 OpenGL ES 中的 GLSL 最高版本為 320,且使用 GLSL ES 時,需要這樣:
#version number es
而 OpenGL 中,3.3 版本發(fā)生了重大更新,所以一般使用 OpenGL 3.3 版本,對應(yīng)的 GLSL 為 330,具體可以上官網(wǎng)查詢。而 OpenGL 中版本聲明一般這么寫:
#version number core
關(guān)鍵就在于版本號和 es 、core 的區(qū)別;
來看看官方文檔中的描述吧~~~
這里是 3.1.0 的版本:GLSL_ES_3.1.0
最新版本為 3.2.0,iOS 中支持的 GLSL ES 最高版本為 3.0.0,但是 version 部分基本沒什么變化,所以直接那 3.1.0 中的文檔來看了;
關(guān)于 #version 的描述如下:

比較重要的幾個結(jié)論:
- 必須聲明在第一行,且必須在所有預(yù)處理命令之前,且必須在注釋之前;
- 如果沒有聲明 version,則默認為 1.0.0 版本;
- 和 OpenGL 中的 GLSL 不同, #version number 后面必須加 es,以此來表明該 GLSL 為 ES 版本;
再來看看 iOS 中的 OpenGL ES,iOS 支持 openGL ES 3.0,相關(guān)文檔如下:

這里其實有待商榷,Apple 官方文檔中沒有找到 OpenGL ES 的具體版本,只知道是 OpenGL ES 3.0 以上的??梢宰鱿聹y試,如果著色器代碼中聲明 #version 320 es 被支持,也就是最新版本的 GLSL ES,是不是可以理解為:iOS 的 OpenGL ES 雖然被遺棄了,但是仍然是最新的?
測試 #version 320 es 和 #version 310 es結(jié)果如下:

測試 #version 300 es,結(jié)果如下:

也就是說,iOS 中的 GLSL ES 版本為 3.0.0。所以,iOS 對應(yīng)的 OpenGL ES 也為 3.0.0,并不是 2019 年修訂的 3.2 版本。
4. Apple 拋棄 OpenGL
iOS 從 iphone7 + iOS12 開始就已經(jīng)拋棄了 OpenGL ES,開始主推自研的 Metal:

這里有個疑問:
已經(jīng)有現(xiàn)成的 openGL ES 了,得罪開發(fā)者,而且自己也需要額外的工作量來開發(fā)新的 Metal 框架,同時還需要底層 GPU 支持自己的 Metal 框架,這么吃力不討好的事情, Apple 為什么這么做?
其實這里可以拆解成幾個問題:
- OpenGL ES 是否已經(jīng)不能滿足 Apple 的需求了?
- 是否真的得罪開發(fā)者?
- Apple 是否在為自己的獨立 GPU 步道?
首先第一個問題,查閱資料之后可以確定,openGL ES 已經(jīng)無法滿足 Apple 了。因為:
- OpenGL ES 由 khronos 制定、發(fā)布、更新,但是該組織由很多公司組成,各公司為了自己的利益在 openGL/openGL ES 中增加擴展,需要得到的部分公司同意之后才能發(fā)布到 release 版本,所以效率極慢;
- openGL 和 openGL ES 已經(jīng)很久沒有更新了,而且官方已經(jīng)使用 Vulken 來替代 openGL 了。嵌入式設(shè)備端, Vulken 已經(jīng)支持嵌入式設(shè)備,并在安卓和 Flutter 上應(yīng)用,只是 Apple 有了 Metal,所以官方并沒有對其進行支持。如果想在 MacOS/iOS 上使用 Vulken,只能使用 khronos 開發(fā)的擴展來進行支持,本質(zhì)上是對 Metal 的封裝;
- OpenGL 擁有很多歷史包袱且支持跨平臺,那么性能必定上無法滿足 Apple 對高性能的追求。加上更新緩慢,Apple 研發(fā)自己的圖形 Api 勢在必行;
所以,對于第一個問題可以做出解答:
OpenGL/OpenGL ES 已經(jīng)有點更不上現(xiàn)在 GPU 的發(fā)展,在性能和、更新周期、Api 設(shè)計上皆無法滿足 Apple 的需求了,所以 Metal 勢在必行;
第二個問題,Apple 是否對開發(fā)者不友好?這個要從幾點來說明:
- Apple 從來都是以自身的發(fā)展方向作為技術(shù)選型的主要考慮,包括后面的自研芯片,因特爾的芯片因為無法跟上自己的需求被 Apple 拋棄轉(zhuǎn)向自研 M1 芯片,Metal 同樣如此;
- DirectX 和 Metal 一樣都是屬于面向 GPU 的圖形 Api,windows 早就這么做了,為什么現(xiàn)在 Apple 這么做就不行?
- OpenGL/OpenGL ES 官方都已經(jīng)拋棄并轉(zhuǎn)向 Vulken 了,這個時候怪 Apple 對開發(fā)者不友好?誰讓你在 Metal 都已經(jīng)武裝完成之后才出來 Vulken,難不成 Apple 還要一直等你或者受制于 khronos?
第三個問題,其實已經(jīng)不用回答了,從 M1 芯片的推出,Apple 幾乎已經(jīng)大通了硬件+軟件的全鏈路,自成一派了,自身的發(fā)展完全由自己掌握,包括后續(xù)的電車、增強現(xiàn)實、虛擬現(xiàn)實、機器學(xué)習(xí)、Aiot 框架等等,都可以由自己來支持。
5. 圖形學(xué)框架常見名詞和分層
常見的名詞有:
- Unity 3D、Cocos3D 、UE4;
- sika;
- Core Animation、Core Graphics
- FlutterUI、MaterialUI、UIKit;
這里以 Flutter 的圖形架構(gòu)圖來展開:

從上圖可以看出,基本上可以這樣分層:
- 圖形 Api
該層是面向 GPU 的圖形 Api,也就是我們上文說到的 Vulken 、DirectX 、Metal、 OpenGL/OpenGL ES。OpenGL 已經(jīng)被拋棄,所以主流的圖形 Api 也就剩下三個了。
從上文可以看到,F(xiàn)lutter 使用 OpenGL ES 對低端機器進行支持,使用 Metal 支持 iOS 平臺,使用 Vulken 支持 Android 平臺。因為 DirectX 主要用于 PC 端,自然不在其框架內(nèi)。
- 中間層
為什么需要中間層?
封裝的主要原因是滿足不同級別的抽象。面向 GPU 的圖形學(xué) Api 不可能直接提供給所有的開發(fā)者,所以系統(tǒng)需要為開發(fā)者提供 UI 框架。但是在開發(fā)這個框架時,也不太可能直接使用圖形 Api,因為這樣會有大量重復(fù)工作。
另外,有很多業(yè)務(wù)是不需要所有的上層開發(fā)者知道的,學(xué)習(xí)成本越低,那么框架的普及就越高效,所以就需要封裝很多流程,比如渲染機制的封裝。
所以中間層誕生了,這個主要供系統(tǒng)開發(fā)人員使用,同時也會部分提供給業(yè)務(wù)開發(fā)者;
如上圖,Core Animation 主要是封裝了渲染管線、CPU 和 GPU 的交互。蘋果的員工在開發(fā) UIKit 時,不需要自己去生成和計算頂點數(shù)據(jù)、著色器代碼,也不需要去調(diào)用很多 openGLES/Metal 的接口就可以完成渲染操作。
skia 作為 Flutter 的核心渲染框架,也是同理。因為 Flutter 是跨平臺的,所以 Skia 內(nèi)部相對于 Core Animation,必定會多出很多跨平臺判斷、兼容的代碼;
- UI 層
這一層就是系統(tǒng) UI 框架的最上層,面向普通的開發(fā)者。比如 iOS 中的 UIKit,MacOS 中的 AppKit,Android 中的 View、Group 等,還有 Flutter 的 Flutter UI;
開發(fā)者可以使用該層架構(gòu)提供的 Api 來快速完成 UI 相關(guān)的業(yè)務(wù),就算是新手也可以使用幾行代碼就可以展示一個 Label、Button 或者是列表;
Unity 3D 沒有使用過,個人理解,Unity 3D 類似于 UIKit 和 AppKit 框架,或者說是介于 UIKit 和 OpenGL/Metal 中間的一個框架。開發(fā)者可以使用 Unity 3D 提供的 Api 做出很多酷炫的動畫和圖形,從而簡化游戲開發(fā)或者是重度繪制 App 的開發(fā)流程。

個人更偏向于把 Unity3D 放在和 Core Animation/Skia 平級,也就是直接操作圖形 Api。Unity3D 作為跨平臺游戲引擎,自然也是完成了對 OpenGL、Metal、Vulken 的封裝。
上文中寫道,不可能面向所有的開發(fā)者提供圖形 Api,關(guān)鍵在于這個所有人的區(qū)分。工具人還是工程師,取決于你對原理了解的多少,對本質(zhì)觸及的多少,這些才最終決定了你是哪一類人。
6. Tips

總結(jié):
- 圖形 Api(graphics Api)是針對 GPU 的,由 GPU 廠商進行支持;GPU 廠商支持的越多,你的標(biāo)準就更容易流行并被開發(fā)者接納,亦或是像蘋果一樣,依靠自身的生態(tài)鏈自成一派;
- 現(xiàn)在的圖形 Api 只有三種:Metal、OpenGL、Vulken;
- 圖形 Api 之上還有中間層,也就是對圖形 Api 的封裝和渲染流程的封裝,有:skia(flutter)、Core Animation/Core Graphic、Canvans??類似的,游戲開發(fā)中的游戲引擎也是處于這一部分,比如 Unity3D、UE4 或 cocos2dx 都是給予對圖形 Api 的封裝而實現(xiàn)的;
- 在中間層之上,還有 UI Framework ,比如 UIKit 就是使用 Core Animation 或者 Core Graphic 實現(xiàn)了一套 UI。類似的還有 Android 和 Flutter 上層的 UI Framework;
- openGL 是最初的圖形 Api,在最早也是所有圖形繪制框架的基礎(chǔ)架構(gòu),跨平臺支持很好。但是隨著嵌入式設(shè)備的普及而嵌入式的 GPU 又沒有 PC 端那么強悍,所以只能在 openGL 上拉出一個子集來支持,也就是 openGL ES;
- openGL 雖然歷史悠久,跨平臺支持很好,但是存在著很多歷史包袱,另外因為跨平臺,if else 自然就會消耗性能,最后,因為 khronos 是有很多公司組成的一個組織,大家都會為了子集的利益各自為戰(zhàn),這就導(dǎo)致 openGL 更新緩慢,適應(yīng)不了一些公司的發(fā)展,所以就出現(xiàn)了 DirectX 和 Metal;
- Vulken 本質(zhì)上是為了 替換 openGL,所以一開始就是跨平臺的。但是 Apple 已經(jīng)有了自己的 Metal 框架,所以暫時沒有對 Vulken 進行支持,這也就讓 Vulken 的跨平臺意義大打折扣,也就讓 Vulken 和 DirectX 成為了競爭對手。
- 當(dāng)前除了 Apple 在 iOS/MacOS 上已經(jīng)完全拋棄了 openGL,其他平臺暫時還處于一種比較糾結(jié)和共存的狀態(tài),但是大部分 GPU 廠商已經(jīng)支持 Vulken 了,這算是一個比較好的地基;
- Windows PC 端,大部分游戲應(yīng)用都是給予 DirectX 開發(fā),少部分已經(jīng)支持 Valken,比如 Dota2;
- 安卓端,仍然是 openGL ES 為主,F(xiàn)lutter 已經(jīng)是 Vulken 和 Metal 為主了?openGL 用于支持低端機型?
- vulkan 相對于 openGL,Api 上更偏向于底層,這意味著可以使用 vulkan 更加自由的操作 GPU 以此來為上層提供更豐富和搞笑的功能。于此同時,開發(fā)者也需要做更多的工作,所以也就出現(xiàn)了圖形 Api 入門程序中,繪制三角形時,vulkan 的代碼遠超過 openGL/openGL ES;
7. iOS 和 OpenGL ES
幾個結(jié)論:
- iOS 上仍然可以使用 OpenGL ES 進行開發(fā),只不過版本最高只能使用 OpenGL ES 3.0;
- iOS 上如果實在對圖形 Api 有需求,趁早轉(zhuǎn) Metal;
- iOS 中做一些渲染優(yōu)化時,最好直接參考 Apple 的官方文檔。因為 Metal 和 OpenGL ES 的流程、設(shè)計思路都不一樣了;
- 要深刻認識封裝、封裝顆粒、Api 通用性設(shè)計的重要性;
比如如果 App 中有圖形 Api 的需求,就應(yīng)該基于 OpenGL ES 進行封裝,產(chǎn)生一套自有的中間框架,而且 Api 應(yīng)當(dāng)盡量屏蔽硬件層的邏輯。如果這樣做了,除非 Metal 不支持,后續(xù)更換 Metal,只需要將 OpenGL ES 的調(diào)用替換成 Metal 即可,相對而言會輕松一些;