1 前言
從這個(gè)系列的第一篇博客到現(xiàn)在也快寫(xiě)一年了,在第一篇文章中對(duì)OpenGL進(jìn)行了整體介紹,后面的博客也陸陸續(xù)續(xù)按照OpenGL程序、數(shù)據(jù)苔埋、OpenGL圖像渲染管道、計(jì)算著色器蜒犯、幀緩存组橄、高級(jí)渲染技術(shù)荞膘、光影藝術(shù)、性能調(diào)優(yōu)這條路線(xiàn)對(duì)OpenGL的整個(gè)知識(shí)樹(shù)有了詳細(xì)的介紹晨炕,現(xiàn)在到了該收尾的時(shí)候了衫画。
在本篇博客中,主要對(duì)OpenGL擴(kuò)展瓮栗,OpenGL適用的平臺(tái)削罩,OpenGL的移動(dòng)版本-OpenGL ES簡(jiǎn)單介紹。
2 擴(kuò)展
到目前為止本系列博客中出現(xiàn)的示例程序都只依賴(lài)了OpenGL的核心版本费奸,然而OpenGL的一個(gè)強(qiáng)大之處在于它的擴(kuò)展模式支持硬件制造商弥激、操作系統(tǒng)供應(yīng)商甚至哪些工具和調(diào)試人員作為發(fā)布者對(duì)其進(jìn)行擴(kuò)展和加強(qiáng)。
一個(gè)擴(kuò)展指的是在某個(gè)官方版本的OpenGL提供的API之外的內(nèi)容愿阐,所有的擴(kuò)展都被列舉在官方網(wǎng)站上微服。在該網(wǎng)站上詳細(xì)的介紹了該擴(kuò)展是對(duì)于哪個(gè)版本的OpenGL開(kāi)發(fā),以及它與該版本的官方文檔有什么區(qū)別缨历。這意味著當(dāng)你使用該擴(kuò)展后以蕴,你當(dāng)前的OpenGL的某些行為和其對(duì)應(yīng)的官方文檔會(huì)有一定的差異。然而流行的以及常用的擴(kuò)展都會(huì)被整合至OpenGL的官方版本之中辛孵,這意味著如果你使用的是最新版本的OpenGL丛肮,則你需要使用的擴(kuò)展可能并不是很多,因?yàn)樗鼈円呀?jīng)成為核心部分魄缚。被整合至核心版本OpenGL的擴(kuò)展清單以及這部分?jǐn)U展對(duì)應(yīng)的功能說(shuō)明被包含在每個(gè)版本的OpenGL官方文檔的末尾宝与,這些文檔都可以在上文列的官方網(wǎng)站上找到。
擴(kuò)展有三分主要的集合冶匹,供應(yīng)商习劫、EXT和ARB(OpenGL架構(gòu)審核委員會(huì))。供應(yīng)商開(kāi)發(fā)的擴(kuò)展在它們所提供的硬件上實(shí)現(xiàn)嚼隘,通過(guò)首字母的縮寫(xiě)來(lái)表示特定的供應(yīng)商诽里,這部分縮寫(xiě)通常在擴(kuò)展簽名中體現(xiàn),如AMD表示AMD公司飞蛹,NV表示英偉達(dá)公司须肆,等等。有時(shí)一個(gè)供應(yīng)商的擴(kuò)展也會(huì)被其他供應(yīng)商支持桩皿,特別是當(dāng)這個(gè)擴(kuò)展被廣泛接受后。EXT就表示該擴(kuò)展被超過(guò)兩個(gè)以上的供應(yīng)商硬件支持幢炸。通常情況下一個(gè)供應(yīng)商以自己的標(biāo)識(shí)注冊(cè)了一個(gè)擴(kuò)展泄隔,另外的供應(yīng)商接受了這個(gè)擴(kuò)展,他聯(lián)合該擴(kuò)展的發(fā)布者對(duì)其進(jìn)行一定修改宛徊,推出EXT版本的擴(kuò)展佛嬉。ARB擴(kuò)展是指被OpenGL架構(gòu)審核委員會(huì)(Architecture Review Board逻澳,ARB)認(rèn)可的擴(kuò)展,它們是OpenGL的官方部分暖呕。它們通常被大多數(shù)或者主流的硬件支持斜做,并且可能是從供應(yīng)商擴(kuò)展和EXT擴(kuò)展演化而來(lái)。
當(dāng)擴(kuò)展被廣泛接受后湾揽,它就會(huì)被包含在下一個(gè)OpenGL的官方版本中瓤逼,這種自然選擇的方式確保了只有有用的和重要的OpenGL擴(kuò)展才能被包含在OpenGL的核心版本中。
Realtech VR發(fā)布的程序OpenGL Extensions Viewer能夠幫助我們了解當(dāng)前電腦的OpenGL實(shí)現(xiàn)能夠支持哪些擴(kuò)展库物,該程序可以從其官方網(wǎng)站上獲取霸旗。
2.1 使用擴(kuò)展加強(qiáng)OpenGL
在使用任何一個(gè)擴(kuò)展之前,你必須確保該擴(kuò)展被當(dāng)前的OpenGL實(shí)現(xiàn)所支持戚揭,通過(guò)調(diào)用函數(shù)glGetIntegerv()
可以查出當(dāng)前支持的擴(kuò)展數(shù)量诱告,接著調(diào)用如下函數(shù)依次查詢(xún)所支持的擴(kuò)展名稱(chēng)。
const GLubyte* glGetStringi(GLenum name, GLuint index);
在上面的函數(shù)中民晒,參數(shù)name
傳入枚舉值GL_EXTENSIONS
精居,參數(shù)value
傳入的值需要大于等于0,并且小于所支持的擴(kuò)展數(shù)潜必。想要確定是否支持某個(gè)擴(kuò)展靴姿,則需要通過(guò)上面的方式遍歷完所有支持的擴(kuò)展名,并和待驗(yàn)證的擴(kuò)展名比較刮便。
引用擴(kuò)展通晨詹拢可以通過(guò)如下方式加強(qiáng)OpenGL。
- 通過(guò)移除某個(gè)版本OpenGL官方文檔中的限制恨旱,使得一些不合法的事變得合法辈毯。
- 它們能添加令牌或者擴(kuò)展已經(jīng)存在的函數(shù)參數(shù)的取值范圍。
- 它們能夠擴(kuò)展著色器語(yǔ)言搜贤,添加一些功能谆沃,內(nèi)建函數(shù),變量或者數(shù)據(jù)類(lèi)型仪芒。
- 它們直接添加了一些新的函數(shù)唁影。
對(duì)于前兩種情況,在程序中不需要做特殊的處理掂名,直接使用在擴(kuò)展中被允許的行為据沈,或者直接使用或者提供的新的參數(shù)取值。新的取值在系統(tǒng)提供的庫(kù)頭文件中并不存在饺蔑,你需要在擴(kuò)展的官方文檔中查詢(xún)锌介。
想要在著色器語(yǔ)言中使用擴(kuò)展,必須在著色器代碼的前面加上一行聲明,告訴編譯器你將會(huì)使用擴(kuò)展提供的特性孔祸。例如隆敢,假如存在一個(gè)名為GL_ABC_foobar_feature的擴(kuò)展,你想要在著色器語(yǔ)言中使用這個(gè)特性崔慧,則你需要在著色器代碼的最前端加上如下聲明拂蝎。
#extension GL ABC foobar feature : enable
這個(gè)聲明告訴OpenGL你打算在著色器中使用該擴(kuò)展的函數(shù),如果編譯器知道這個(gè)擴(kuò)展惶室,該著色器就能被成功編譯温自,即使底層的硬件并不支持這個(gè)擴(kuò)展。對(duì)于這種情況拇涤,如果編譯器檢查到你真正使用了該擴(kuò)展內(nèi)部的功能捣作,將會(huì)拋出警告信息。另外GLSL所支持的擴(kuò)展都會(huì)添加預(yù)編譯標(biāo)記以表明它們是被支持的鹅士,例如如果GL_ABC_foobar_feature被支持券躁,則會(huì)隱式的包含如下定義。
#define GL ABC foobar feature 1
這意味著你可以按如下方式編寫(xiě)代碼掉盅。
#if GL ABC foobar feature
// 使用擴(kuò)展foobar中的函數(shù)
#else
// 使用其他函數(shù)
#endif
這種方式使我們可以條件編譯或者執(zhí)行一個(gè)擴(kuò)展中的函數(shù)也拜,盡管這個(gè)擴(kuò)展可能不被底層的OpenGL實(shí)現(xiàn)所支持。如果你的著色器必須要求底層的OpenGL實(shí)現(xiàn)支持某個(gè)擴(kuò)展才能夠正常工作趾痘,則你可以按如下方式聲明慢哈。
#extension GL ABC foobar feature : require
此時(shí)如果底層的OpenGL不支持這個(gè)擴(kuò)展,則著色器不能被成功編譯永票,并且會(huì)在聲明擴(kuò)展的行報(bào)錯(cuò)卵贱。實(shí)際上OpenGL擴(kuò)展的聲明是可選的,也就是大多數(shù)OpenGL實(shí)現(xiàn)默認(rèn)會(huì)啟用一些擴(kuò)展中的函數(shù)侣集,但是如果你依靠這種默認(rèn)實(shí)現(xiàn)键俱,你的程序很可能在部分OpenGL的驅(qū)動(dòng)中無(wú)法工作,因此當(dāng)你想要使用某個(gè)擴(kuò)展時(shí)世分,務(wù)必先聲明擴(kuò)展编振。
對(duì)于最后一種擴(kuò)展形式,即引入了新的函數(shù)臭埋。在大多數(shù)平臺(tái)上踪央,你都不能夠直接訪(fǎng)問(wèn)OpenGL驅(qū)動(dòng)器,擴(kuò)展中的函數(shù)也不會(huì)魔法般的出現(xiàn)供你調(diào)用瓢阴。相反畅蹂,你需要向OpenGL驅(qū)動(dòng)器獲取一個(gè)你需要調(diào)用的函數(shù)指針。函數(shù)指針的定義通常包含兩部分荣恐,第一部分是函數(shù)指針類(lèi)型的定義魁莉,第二部分是函數(shù)指針變量的定義,示例如下。
typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id);
PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback = NULL;
上面的代碼塊聲明了一個(gè)函數(shù)指針類(lèi)型PFNGLDRAWTRANSFORMFEEDBACKPROC旗唁,該函數(shù)需要一個(gè)GLenum以及一個(gè)GLuint類(lèi)型的參數(shù)。接著聲明了一個(gè)該函數(shù)指針類(lèi)型的變量glDrawTransformFeedback痹束。實(shí)際上在大多數(shù)平臺(tái)中检疫,函數(shù)glDrawTransformFeedback()就是如此聲明的。這種方式看上去十分繁瑣祷嘶,幸運(yùn)的是屎媳,如下的頭文件中已經(jīng)包含了所有注冊(cè)的OpenGL中的擴(kuò)展的函數(shù)原型、函數(shù)指針類(lèi)型以及標(biāo)示變量的聲明论巍,我們只需要導(dǎo)入這些頭文件即可烛谊。
#include <glext.h>
#include <glxext.h>
#include <wglext.h>
在OpenGL的官方注冊(cè)網(wǎng)站可以找到上面的頭文件,頭文件glext.h
中包含了標(biāo)準(zhǔn)的OpenGL擴(kuò)展和很多供應(yīng)商提供的OpenGL擴(kuò)展嘉汰,頭文件wglext.h
中包含了很多Windows平臺(tái)的擴(kuò)展丹禀,頭文件glxext.h
中則包含了X提供支持的擴(kuò)展,X系統(tǒng)是使用在Linux和很多其他的Unix衍生物和實(shí)現(xiàn)中的一個(gè)窗口系統(tǒng)鞋怀。
查詢(xún)函數(shù)指針的方法和特定平臺(tái)的實(shí)現(xiàn)相關(guān)双泪,這里不做具體介紹∶芩疲可以參考原書(shū)中的函數(shù)void * sb6GetProcAddress(const char * funcname);
焙矛。
需要注意的是當(dāng)獲取到某個(gè)擴(kuò)展中函數(shù)的指針時(shí),并不表明當(dāng)前的OpenGL實(shí)現(xiàn)支持該擴(kuò)展残腌,因?yàn)橛袝r(shí)多個(gè)擴(kuò)展中會(huì)定義相同的函數(shù)村斟,有時(shí)供應(yīng)商發(fā)布的驅(qū)動(dòng)器只包含某個(gè)擴(kuò)展的部分實(shí)現(xiàn)。因此在確定是否支持某個(gè)擴(kuò)展時(shí)抛猫,請(qǐng)使用前文的標(biāo)準(zhǔn)函數(shù)蟆盹。
3 平臺(tái)
OpenGL時(shí)一個(gè)強(qiáng)大的接口集合,它較底層的天性使得程序開(kāi)發(fā)著能夠控制更多細(xì)節(jié)邑滨。另外核心的OpenGL代碼具有跨平臺(tái)和跨操作系統(tǒng)的可移植性日缨。因?yàn)椴煌牟僮飨到y(tǒng)都有不同的管理窗口的方式,因此每個(gè)操作系統(tǒng)也有不同的層來(lái)幫助程序和OpenGL交互掖看。這種方式幫助來(lái)驅(qū)動(dòng)實(shí)現(xiàn)理解緩存的類(lèi)型匣距,顏色的格式以及其他應(yīng)該被用于特定目的的特征。
本系列博客是在最開(kāi)始的時(shí)候已經(jīng)介紹過(guò)iOS和Mac OS平臺(tái)上如何開(kāi)發(fā)OpenGL程序哎壳,關(guān)于在其他各個(gè)平臺(tái)上使用OpenGL的細(xì)節(jié)這里不會(huì)覆蓋毅待。
3.1 Windows
在微軟公司的Windows操作系統(tǒng)中,提供了一套和操作系統(tǒng)相綁定的函數(shù)集合归榕,被稱(chēng)為WGL(Windows-GL)尸红。WGL函數(shù)方法簽名中都包含wgl前綴,在標(biāo)識(shí)中都包含WGL_的前綴。
3.2 Mac OS
在蘋(píng)果公司的Mac OS平臺(tái)上外里,最初OpenGL是蘋(píng)果推薦以及系統(tǒng)底層所依賴(lài)的API怎爵,但在蘋(píng)果公司自己的圖像處理庫(kù)Metal發(fā)布后,和OpenGL相關(guān)的接口已經(jīng)被標(biāo)記為廢棄盅蝗,但是你仍然能夠使用這些API來(lái)編寫(xiě)并發(fā)布你的應(yīng)用程序鳖链。
蘋(píng)果對(duì)于OpenGL版本的支持速度總是很慢的,著其中有一個(gè)原因是因?yàn)樘O(píng)果將用戶(hù)的體驗(yàn)放在了很高的位置墩莫,蘋(píng)果公司的工程師們看重的是一個(gè)程序的統(tǒng)一性芙委,它能夠運(yùn)行在當(dāng)前的主流硬件之上。因此如果你的應(yīng)用是只為蘋(píng)果的生態(tài)開(kāi)發(fā)的狂秦,那么選擇Metal吧灌侣,如果你想要開(kāi)發(fā)一個(gè)程序不用考慮太多和圖像顯卡以及驅(qū)動(dòng)程序相關(guān)的事,可以選擇蘋(píng)果平臺(tái)裂问,如果你想使用最新的OpenGL功能侧啼,那么換個(gè)平臺(tái)吧。也正因?yàn)樵贛ac OS中最新版的OpenGL并沒(méi)有得到支持愕秫,因此本系列博客的部分技術(shù)點(diǎn)的示例程序在該平臺(tái)上無(wú)法實(shí)現(xiàn)慨菱。
3.3 Linux
在Linux、Unix等類(lèi)似平臺(tái)的大量版本中戴甩,OpenGL已經(jīng)成為3D圖形渲染的跳轉(zhuǎn)API符喝。Linux提供了多種方式訪(fǎng)問(wèn)OpenGL,大多數(shù)主流的圖形硬件也提供一些硬件加速方案甜孤。Mesa3D是一種OpenGL的軟件實(shí)現(xiàn)形式协饲,它不依賴(lài)于圖形處理硬件,能夠被安裝在大多數(shù)X系統(tǒng)的服務(wù)器上缴川。
在八十年代末期茉稠,硅谷圖形公司(Silicon Graphics, SGI)采用了一套專(zhuān)有的二維和三維圖形渲染API,它被運(yùn)行在IRIS GL(Integrated Raster Imaging System Graphics Library)工作站之上把夸。在1992年而线,硅谷圖形公司修訂了官方文檔,并將其作為一個(gè)開(kāi)放的工業(yè)標(biāo)準(zhǔn)公布恋日,將其命名為OpenGL膀篮。在1993年,Brian Paul創(chuàng)立了一個(gè)項(xiàng)目Mesa3D岂膳,它是對(duì)OpenGL的軟件實(shí)現(xiàn)誓竿,使得3D圖形渲染能夠被更容易的使用,不再依賴(lài)于特定的硬件供應(yīng)商谈截。Mesa最近的版本使用了Gallium3D架構(gòu)使其能夠支持硬件加速筷屡,并且這種硬件加速實(shí)現(xiàn)通常都是可用的涧偷。
現(xiàn)在可用的大多數(shù)計(jì)算機(jī)系統(tǒng)都包含某種形式的3D加速方案。現(xiàn)代3D圖形硬件供應(yīng)商通過(guò)向Mesa或者其他開(kāi)源工程提供支持毙死,或者通過(guò)發(fā)布最近的特定硬件的驅(qū)動(dòng)程序的方式來(lái)支持最新版本的OpenGL燎潮≌芭簦總的來(lái)說(shuō)發(fā)布驅(qū)動(dòng)的方式總能夠快速的跟進(jìn)OpenGL版本的發(fā)布势誊。
3.4 移動(dòng)平臺(tái)
通過(guò)精簡(jiǎn)OpenGL而設(shè)計(jì)了其移動(dòng)平臺(tái)版本OpenGL ES,目前最新的版本是OpenGL ES 3.0起趾。
3.4.1 OpenGL瘦身
OpenGL ES和OpenGL很相似唉锌,它的官方文檔是通過(guò)多個(gè)版本的OpenGL整理出來(lái)的。OpenGL提供了一套很完整的3D渲染接口竿奏,這套接口非常靈活袄简,并且可以用于開(kāi)發(fā)不同類(lèi)型的應(yīng)用程序,從游戲到CAD制圖軟件以及醫(yī)學(xué)程序程序都有涉及泛啸。
隨著時(shí)間的發(fā)展绿语,OpenGL相關(guān)的API因?yàn)樾枰С指嗟墓δ芏粩U(kuò)展,這使得整個(gè)API變得異常臃腫候址,甚至同一個(gè)功能會(huì)有多個(gè)不同的方式實(shí)現(xiàn)吕粹,因此在最新的版本的OpenGL中已經(jīng)采取激進(jìn)的方式移除了一些過(guò)時(shí)的特性。而OpenGL ES精簡(jiǎn)的OpenGL提供的特性集合岗仑,只包含一些常用特性匹耕,它是OpenGL的一個(gè)子集。OpenGL ES 3.0在靈活性和可用行上坐了很好的平衡荠雕,更適用于嵌入式和移動(dòng)平臺(tái)環(huán)境稳其。
隨著硬件成本變得更低,越來(lái)越多的功能能夠在較小的半導(dǎo)體空間內(nèi)實(shí)現(xiàn)炸卑,對(duì)于嵌入式的設(shè)備既鞠,用戶(hù)界面也變得越來(lái)越復(fù)雜。最常見(jiàn)的例子就是汽車(chē)盖文,在80年代默契第一個(gè)對(duì)于汽車(chē)計(jì)算機(jī)可視化反饋系統(tǒng)通過(guò)以單行或者多行文字的方式提供嘱蛋。這些界面提供如安全帶使用,當(dāng)前的燃油剩余了五续,形式里程等信息洒敏。后來(lái)顯示屏開(kāi)始流行,這些設(shè)備通常以位圖的方式向用戶(hù)傳遞信息返帕。最近具有3D功能的系統(tǒng)也被整合到了汽車(chē)中桐玻,它能夠向用戶(hù)提供GPS導(dǎo)航,環(huán)境控制荆萤,娛樂(lè)以及其他密切依賴(lài)圖形處理的功能镊靴。實(shí)際上在很多更新型號(hào)汽車(chē)上的設(shè)備是采用OpenGL ES 2.0來(lái)完成圖形渲染的铣卡。對(duì)于航空儀器和手機(jī)而言也存在同樣的科技演變過(guò)程。
早期的嵌入式3D圖形接口通常和特定的硬件密切相關(guān)偏竟,這是因?yàn)楸恢С止δ芗贤ǔ]^簡(jiǎn)單煮落,并且在不同的設(shè)備之間有很大的差異。隨著每個(gè)供應(yīng)商的3D引擎變得越來(lái)越復(fù)雜踊谋,想要在不同設(shè)備之間移植程序就會(huì)十分耗時(shí)蝉仇,并且充滿(mǎn)挑戰(zhàn)。唯一的解決辦法就是定義一套標(biāo)準(zhǔn)的接口殖蚕,而負(fù)責(zé)維護(hù)這個(gè)標(biāo)準(zhǔn)的組織就是Khronos Group轿衔。
3.4.2 Khronos
Khronos Group在2000年有OpenGL架構(gòu)審核委員會(huì)的成員成立。當(dāng)時(shí)PC端已經(jīng)存在很多兼容的圖形處理接口睦疫,但是Khronos的目的是想要定義一套能夠在個(gè)人電腦設(shè)備之外更加實(shí)用的接口害驹。它們開(kāi)放的第一套嵌入式API被就是OpenGL ES。Khronos由很多在硬件和軟件工業(yè)的領(lǐng)頭者組成蛤育,當(dāng)前的成員包括AMD宛官、Apple、ARM瓦糕、Intel底洗、Google、NVIDIA和高通等知名公司咕娄,其完整的成員可以在官方網(wǎng)站查詢(xún)亥揖。
3.4.3 歷史版本
第一個(gè)OpenGL ES版本是OpenGL ES 1.0,它以O(shè)penGL 1.3的標(biāo)準(zhǔn)定義為基礎(chǔ)谭胚,精簡(jiǎn)了大部分特性徐块,它同樣采用的是固定管線(xiàn)策略,即頂點(diǎn)轉(zhuǎn)換和片段處理是不可編程的灾而。OpenGL ES SC 1.0時(shí)一個(gè)專(zhuān)為可靠性要求極高的場(chǎng)景設(shè)計(jì)的版本胡控,這種環(huán)境中湯匙講安全性認(rèn)為是至關(guān)重要的(Safety Critical),因此加上SC標(biāo)示符旁趟。典型的成員應(yīng)用在航空昼激、汽車(chē)和軍事領(lǐng)域,在這些場(chǎng)景中锡搜,3D程序通常用于儀表可視化橙困,繪圖和地形繪制。
ES 1.1基于OpenGL 1.5標(biāo)準(zhǔn)定義開(kāi)發(fā)耕餐,它和ES 1.0版本類(lèi)似凡傅,僅僅增加了如緩存對(duì)象、紋理繪制等一些有趣的新特性肠缔。
ES 2.0基于OpenGL 2.0規(guī)范開(kāi)發(fā)夏跷,它和之前的版本不兼容哼转。這個(gè)版本最大的改變就是將固定管線(xiàn)部分完全移除,而是采用可編程的著色器來(lái)執(zhí)行頂點(diǎn)和片段處理邏輯槽华。ES 2.0采用了OpenGL ES著色器語(yǔ)言(OpenGL ES Shading Language)壹蔓,它和OpenGL 2.0+相匹配的OpenGL著色器語(yǔ)言非常相似。
最新版本的OpenGL ES是3.0猫态,它向后兼容OpenGL ES 2.0佣蓉。這個(gè)版本從多個(gè)版本的OpenGL中移植了大量的特性,另外新版的著色器語(yǔ)言O(shè)penGL ES SL 3.0也擴(kuò)展了著色器的能力亲雪,OpenGL ES 3.0也支持較早版本的著色器語(yǔ)言勇凭。
OpenGL ES和其參考的OpenGL規(guī)范對(duì)應(yīng)關(guān)系如下表。
OpenGL ES 版本 | OpenGL 版本 |
---|---|
OpenGL ES 1.0 | OpenGL 1.3 |
OpenGL ES SC 1.0 | OpenGL 1.3 |
OpenGL ES 1.1 | OpenGL 1.5 |
OpenGL ES 2.0 | OpenGL 2.0 |
OpenGL ES 3.0 | OpenGL 4.0+ |
通常情況下硬件是為某個(gè)特定版本的API制造的义辕,這些平臺(tái)可能僅支持OpenGL ES的某個(gè)版本套像。考慮不同的OpenGL ES版本的差異是有益的终息,不同版本的ES表示了不同底層依賴(lài)硬件具備的能力。
對(duì)于OpenGL而言贞让,新的硬件通常都會(huì)支持到最新版本周崭。但是對(duì)于OpenGL ES則不同,新硬件采用的特性類(lèi)型都是基于多個(gè)因素考量的喳张,如針對(duì)性的產(chǎn)品成本续镇,典型的用途,以及系統(tǒng)的支持等销部。在最近5年半導(dǎo)體科技取得了很大突破摸航,現(xiàn)在已經(jīng)能夠生產(chǎn)非常小型,成本劃算的以及高性能的芯片舅桩。幾乎所有的智能手機(jī)如谷歌公司研發(fā)的Android系統(tǒng)酱虎,和蘋(píng)果公司研發(fā)的iPhone手機(jī)都采用OpenGL ES作為圖形渲染API。
本小節(jié)不會(huì)介紹之前歷史版本的OpenGL ES擂涛,而是會(huì)聚焦在OpenGL ES 3.0读串,另外這里不會(huì)再重復(fù)介紹圖形渲染接口的特性,這部分在前面的章節(jié)中已經(jīng)覆蓋撒妈,這里只是介紹這些功能在OpenGL ES上和OpenGL上的差異恢暖。
3.4.4 OpenGL ES 3.0
OpenGL ES 3.0和OpenGL 4.3在API的設(shè)計(jì)上驚人的一致,它們都移除了過(guò)時(shí)的接口狰右,具有更精簡(jiǎn)的接口集合杰捂。不同的是OpenGL 4.3添加了很多新特性,這部分特性并不被在嵌入式硬件支持棋蚌。另外集合著色器嫁佳,曲面細(xì)分著色器挨队,計(jì)算著色器,浮點(diǎn)型的緩存對(duì)象以及其他在OpenGL上的新特性要在當(dāng)前的移動(dòng)設(shè)備或者嵌入式硬件上實(shí)現(xiàn)會(huì)太復(fù)雜脱拼,因此這部分功能并為被添加到OpenGL ES中瞒瘸。但是水準(zhǔn)時(shí)間的遷移,移動(dòng)版本硬件和全功能的桌面級(jí)別的圖形硬件之間的界限會(huì)越來(lái)越模糊熄浓。正如平板電腦究竟應(yīng)該被歸類(lèi)為移動(dòng)設(shè)備還是桌面電腦情臭?手持式游戲設(shè)備和汽車(chē)上娛樂(lè)設(shè)備的界限在哪兒?因此我們能夠看見(jiàn)嵌入式硬件的能力會(huì)逐漸增強(qiáng)赌蔑,直至能夠使用所有OpenGL的功能俯在。
頂點(diǎn)處理和著色
渲染的第一步是定義幾何圖元的頂點(diǎn),頂點(diǎn)規(guī)范要求必須使用頂點(diǎn)緩存對(duì)象娃惯,或者頂點(diǎn)數(shù)組對(duì)象跷乐。可以使用和OpenGL 4.3同樣的方式映射頂點(diǎn)緩存對(duì)象趾浅。通過(guò)調(diào)用函數(shù)glVertexAttribPointer()
指定頂點(diǎn)屬性愕提。
void glVertexAttribPointer(GLuint index,
GLuint size,
GLenum type,
GLboolean normalized,
sizei stride,
const void *ptr);
渲染圖形可以調(diào)用的函數(shù)有glDrawArrays()
、glDrawArraysInstanced()
皿哨、glDrawElements()
浅侨、glDrawElementsInstanced()
和glDrawRangeElements()
。在OpenGL4.3中可以使用的函數(shù)glMultiDrawArrays()
证膨、glMultiDrawElements()
等在OpenGL ES 3.0中并不可用如输。另外OpenGL ES 3.0支持頂點(diǎn)數(shù)組對(duì)象,該對(duì)象用于管理頂點(diǎn)緩存對(duì)象央勒,可以通過(guò)函數(shù)glGenVertexArrays()
不见、glDeleteVertexArrays()
和glBindVertexArray()
操作,具體細(xì)節(jié)在前面的章節(jié)已經(jīng)覆蓋崔步。
著色器
OpenGL ES 2.0和3.0都使用了和OpenGL 4.3相似的可編程著色器稳吮,但是僅僅支持頂點(diǎn)著色器和片段著色器。它們使用的著色器語(yǔ)言也和GLSL語(yǔ)言規(guī)范類(lèi)似井濒,被稱(chēng)為OpenGL ES著色器語(yǔ)言盖高,但針對(duì)嵌入式環(huán)境和硬件做了一定調(diào)整。
多年之前眼虱,當(dāng)OpenGL ES 2.0剛開(kāi)始流行時(shí)喻奥,通常移動(dòng)平臺(tái)不會(huì)包含內(nèi)置的編譯器。這些平臺(tái)依靠程序被開(kāi)發(fā)以及為不同平臺(tái)發(fā)布二進(jìn)制包時(shí)去編譯著色器∧笮現(xiàn)在大多數(shù)移動(dòng)平臺(tái)都包含一個(gè)內(nèi)置的編譯器撞蚕,但是一些嵌入式的環(huán)境可能仍然不支持運(yùn)行時(shí)編譯著色器。
不管怎樣过牙,在OpenGL ES中使用著色器的方式和在OpenGL中類(lèi)似甥厦。相同的程序語(yǔ)意和著色器管理仍然有效纺铭。使用可編程圖形管道的第一步是調(diào)用如下函數(shù)創(chuàng)建必要的著色器對(duì)象。
GLuint glCreateShader(GLenum type);
接下來(lái)以字符串方式加載著色器源碼刀疙,并使用和OpenGL 4.3類(lèi)似的函數(shù)編譯著色器舶赔。
void glShaderSource(GLuint shader,
GLsizei count,
const char **string,
const GLint *length);
void glCompileShader(GLuint shader);
如果你運(yùn)行的平臺(tái)僅僅支持二進(jìn)制的著色器,你需要在這里加載已經(jīng)預(yù)編譯好的二進(jìn)制數(shù)據(jù)而不是源碼谦秧。一種在運(yùn)行時(shí)檢查支持的二進(jìn)制格式方式是查詢(xún)GL_NUM_SHADER_BINARY_FORMATS
竟纳。更多的細(xì)節(jié)需要參考特定設(shè)備的SDK官方文檔。如果片段著色器和頂點(diǎn)著色器時(shí)離線(xiàn)合并編譯的疚鲤,則僅需加載一個(gè)片段-頂點(diǎn)對(duì)二進(jìn)制資源锥累。
void glShaderBinary(GLsizei count,
const GLuint *shaders,
GLenum binaryformat,
const void *binary,
GLsizei length);
所有的平臺(tái)都必須支持源代碼或者二進(jìn)制資源中的一種格式。OpenGL ES 3.0要求必須支持運(yùn)行時(shí)的編譯器集歇,二進(jìn)制的著色器方式是可選項(xiàng)桶略。瀏覽你需要開(kāi)發(fā)的設(shè)備的文檔,確定哪種格式是最高效的诲宇。如果你開(kāi)發(fā)的是Android或者iOS程序际歼,可以直接使用源代碼的方式。
上面的代碼塊執(zhí)行完畢后后姑蓝,調(diào)用如下函數(shù)創(chuàng)建程序?qū)ο蟮磐Γ⒅鲗?duì)象附著在程序?qū)ο笾希缓髣h除著色器對(duì)象它掂。
GLuint glCreateProgram(void);
glAttachShader(GLuint program, GLuint shader);
public func glDeleteShader(_ shader: GLuint)
當(dāng)著色器被加載并編譯,程序也附著這些著色器后溯泣,通過(guò)在著色器中聲明的頂點(diǎn)屬性字符串為參數(shù)虐秋,調(diào)用如下函數(shù)綁定頂點(diǎn)屬性和對(duì)應(yīng)的索引。
glBindAttribLocation(GLuint program, GLuint index, const char *name);
需要注意的是在OpenGL ES 3.0中可用直接在著色器中通過(guò)修飾符直接管理頂點(diǎn)屬性和索引垃沦,不需要調(diào)用上述函數(shù)客给。
在完成上述工作后需要鏈接程序。
glLinkProgram(GLuint program);
如果你定義了統(tǒng)一變量肢簿,需要在這之后調(diào)用如下函數(shù)獲取這些統(tǒng)一變量在當(dāng)前程序中的索引靶剑。
public func glGetUniformLocation(_ program: GLuint,
_ name: UnsafePointer<GLchar>!) -> GLint
當(dāng)上述邏輯都成功執(zhí)行后,接下來(lái)在每次繪制場(chǎng)景時(shí)池充,你可以通過(guò)函數(shù)glUseProgram()
切換你需要使用的OpenGL程序桩引。你可以使用在OpenGL4.3中更新統(tǒng)一變量類(lèi)似的方法在OpenGL ES中完成同樣的操作。需要注意的是在更新矩陣類(lèi)型統(tǒng)一變量值的時(shí)候收夸,參數(shù)transpose
必須設(shè)置為GL_FALSE
坑匠,矩陣轉(zhuǎn)置的功能并不是必須的,因此不被OpenGL ES支持卧惜。下面是一些更新統(tǒng)一變量值的函數(shù)原型厘灼。
void glUseProgram(GLuint program);
void glUniform{1234}{if}(GLint location, T values);
void glUniform{1234}{if}v(GLint location, GLsizei count, T value);
void glUniformMatrix{234}fv(GLint location, GLsizei count,
GLboolean transpose, T value);
另外夹纫,在OpenGL ES 3.0中也支持統(tǒng)一閉包,和它相關(guān)的交互函數(shù)如下设凹。
glGetUniformBlockIndex(GLuint program, const char *uniformBlockName);
glGetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex,
GLsizei bufSize, GLsizei *length,
char *uniformBlockName);
實(shí)際上OpenGL ES 3.0的追哦色全語(yǔ)言和GLSL 3.3幾乎相同舰讹,你可以在PC或者M(jìn)ac環(huán)境中開(kāi)發(fā)著色器,并對(duì)其做很少的修改就能夠移植到移動(dòng)平臺(tái)闪朱。
盡管OpenGL ES 3.0原生不支持幾何和曲面細(xì)分著色器月匣,但是它支持轉(zhuǎn)換反饋模式。這種渲染模式是你可以捕獲頂點(diǎn)著色器的輸出监透,并將其直接存入到緩存對(duì)象中桶错。這樣你可以建立一個(gè)工程只包含頂點(diǎn)著色器用于處理復(fù)雜的頂點(diǎn)處理邏輯,并在合適的時(shí)候使用這部分?jǐn)?shù)據(jù)胀蛮。在前面的章節(jié)中已經(jīng)對(duì)頂點(diǎn)反饋?zhàn)隽嗽敿?xì)的介紹院刁,這里不再深入展開(kāi)。在OpenGL ES 3.0中粪狼,你可以通過(guò)如下函數(shù)使用這種頂點(diǎn)渲染模式退腥。
void glGenTransformFeedback(GLsizei n, GLuint *ids);
void glDeleteTransformFeedback(GLsizei n, const uint *ids);
void glBindTransformFeedback(GLenum target, GLunit id);
void glBeginTransformFeedback(GLenum primitiveMode);
void glEndTransformFeedback();
void glPauseTransformFeedback();
void glResumeTransformFeedback();
光柵化
抗鋸齒線(xiàn)條在OpenGL ES中不支持,另外OpenGL ES 3.0也不支持多變形平滑再榄,多邊形抗鋸齒以及多重多邊形模式狡刘。
紋理
在OpenGL ES 3.0中,2D紋理困鸥,2D紋理數(shù)組嗅蔬,3D紋理以及立方體貼圖都被支持,另外它們對(duì)應(yīng)的采樣器對(duì)象也被支持疾就。關(guān)于采樣器對(duì)象也在前面章節(jié)中講過(guò)澜术,這里不再重復(fù)。OpenGL ES同樣支持一種新的方式一次為整個(gè)紋理分配顯存猬腰,這種方式會(huì)減少驅(qū)動(dòng)器在加載紋理時(shí)的校驗(yàn)工作鸟废,能夠提升紋理加載速度。其函數(shù)原型如下姑荷。
void glTextureStorage2D(GLenum target,
GLsizei levels,
GLenum internalformat,
GLsizei width,
GLsizei height);
ES 3.0所支持的紋理格式相對(duì)于ES 3.0已經(jīng)擴(kuò)展了很多盒延,但是相對(duì)于OpenGL 4.3仍然較少,在使用一個(gè)紋理格式之前鼠冕,首先先確認(rèn)在你使用的OpenGL ES版本中該格式是否可用添寺。
幀緩存
和OpenGL4.3類(lèi)似,OpenGL ES 3.0也支持幀緩存和渲染緩存對(duì)象懈费。應(yīng)用可以創(chuàng)建并綁定自己的幀緩存對(duì)象畦贸,在上面附著渲染緩存或者紋理對(duì)象來(lái)完成離屏渲染工作。和ES 2.0相比,ES 3.0允許使用多重采樣的渲染緩存薄坏,也支持在幀緩存對(duì)象中使用深度紋理趋厉。同樣你可以將一個(gè)紋理的任意一層附著到幀緩存對(duì)象中。
片段操作
在OpenGL ES 3.0中對(duì)于逐片段操作也有一些差異胶坠,它要求至少存在一個(gè)可用的能夠支持深度緩存和模版緩存的配置君账。這樣保證了依賴(lài)深度信息和模版比較的應(yīng)用能夠運(yùn)行在任何支持OpenGL ES 3.0的實(shí)現(xiàn)上。
一些在OpenGL 4.3規(guī)范中的特性也被OpenGL ES移除了沈善。首先乡数,透明度測(cè)試功能被移除,ES將這部分工作轉(zhuǎn)移給片段著色器闻牡。函數(shù)glLogicOp()
不再被支持净赴。只有新的Boolean類(lèi)型的遮蔽查詢(xún)機(jī)制稱(chēng)為ES規(guī)范的一部分。Boolean遮蔽查詢(xún)操作和它在OpenGL 4.3上的工作模式類(lèi)似罩润,但是它不再返回通過(guò)管道的圖元數(shù)量玖翅,而是僅僅告訴你是否有圖元通過(guò)了圖形管道。
盡管混合模式在ES中仍然得以保留割以,但是已經(jīng)被精簡(jiǎn)了金度。混合模式不能再為每個(gè)渲染靶點(diǎn)做不同的配置严沥,并且雙源混合也不再被支持猜极。
狀態(tài)
OpenGL ES 3.0仍然支持和OpenGL 4.3類(lèi)似的狀態(tài)查詢(xún)機(jī)制。你可以使用函數(shù)glGetBooleanv()
消玄、glGetIntegerv()
跟伏、和glGetFloatv()
查詢(xún)大多數(shù)狀態(tài),另外ES 3.0也支持函數(shù)glGetInteger64v()
翩瓜。
3.4.5 OpenGL ES環(huán)境
嵌入式或者移動(dòng)端的硬件資源較PC端更緊張受扳,因此在這種環(huán)境中開(kāi)發(fā)OpenGL程序和PC端環(huán)境有很大不同。
程序設(shè)計(jì)考慮事項(xiàng)
OpenGL ES時(shí)間跨度很廣奥溺,可能存在各種各樣的硬件配置。其中能力最強(qiáng)的可能是擁有特別設(shè)計(jì)圖形顯卡的多核系統(tǒng)骨宠,如索尼的PlayStation 3浮定,或者也可能是一部入門(mén)級(jí)的智能手機(jī),這個(gè)設(shè)備僅有一顆工作頻率為1到2GHz的處理器以及1GB的內(nèi)存层亿。
在有限的資源背景下桦卒,指令的數(shù)量應(yīng)該特別關(guān)注。一些特定的操作可能會(huì)很慢匿又,比如計(jì)算一個(gè)角度的正弦值方灾,和調(diào)用函數(shù)sin()
相比,在一個(gè)預(yù)先準(zhǔn)備好的查詢(xún)表中查詢(xún)近似值會(huì)快很多≡3ィ總而言之洞慎,將PC程序使用的算法遷移到嵌入式系統(tǒng)中都需要精心優(yōu)化。一個(gè)例子是物理仿真計(jì)算嘿棘,這些計(jì)算通常具有很高的性能成本劲腿,它們通常能夠被簡(jiǎn)化以求得近似值從而被部署在如移動(dòng)電話(huà)等嵌入式系統(tǒng)中。
ARM架構(gòu)的CPU占據(jù)了大多數(shù)的嵌入式環(huán)境鸟妙,特別是幾乎所有的移動(dòng)電話(huà)和平板電腦焦人。這種現(xiàn)狀能夠減輕在多平臺(tái)間移植程序的負(fù)擔(dān),但是相較于桌面系統(tǒng)不同的指令集和性能情況仍然是一個(gè)挑戰(zhàn)重父。ARM處理器和移動(dòng)系統(tǒng)被認(rèn)為比傳統(tǒng)的計(jì)算機(jī)在能耗上有更高的效率花椭,通常一次充電就能使用一天甚至更長(zhǎng)的時(shí)間。但是這也意味著程序的能耗也是我們需要關(guān)注的一個(gè)點(diǎn)房午。通常情況下矿辽,性能和能耗需要權(quán)衡,但是在移動(dòng)平臺(tái)上開(kāi)發(fā)程序是需要避免不必要的浪費(fèi)歪沃。使用遮蔽查詢(xún)等手段判斷圖元是否是不可見(jiàn)的嗦锐,這樣能夠幫助我們優(yōu)化程序能耗。
應(yīng)對(duì)有限的資源
在開(kāi)發(fā)嵌入式系統(tǒng)的OpenGL程序是沪曙,不僅開(kāi)發(fā)環(huán)境是受限的奕污,和PC端相比,圖像顯卡處理的能力也是不足的液走。
為存儲(chǔ)空間做好預(yù)算可能有一定的幫組碳默。你可以將可用的最大的顯卡和系統(tǒng)內(nèi)存分解成多個(gè)碎片,并將其用于內(nèi)存密集的分類(lèi)缘眶。這種方式可以提供一個(gè)角度觀察不同的應(yīng)用片段能夠使用的內(nèi)存嘱根,以及這些內(nèi)存何時(shí)被耗盡。最明顯的內(nèi)存密集分類(lèi)就是紋理采樣巷懈。大尺寸该抒,詳細(xì)的紋理能夠在PC端的程序中提供豐富的,細(xì)膩的環(huán)境特征顶燕。這樣做有利于提升用戶(hù)體驗(yàn)凑保,但是在大多數(shù)嵌入式的系統(tǒng)中,紋理是一個(gè)吞噬存儲(chǔ)資源的怪獸涌攻。這些場(chǎng)景在大量片段采樣甚至多重采樣時(shí)能夠造成很大的性能損耗欧引,特別是當(dāng)每個(gè)重疊的幾何圖元片段都被采樣并且以錯(cuò)誤的順序繪制的時(shí)候。
除了核心硬件紋理采樣性能之外恳谎,紋理的尺寸也是一個(gè)主要的限制芝此。3D和立方體紋理都很容易快速占用大量的顯存憋肖,這也是3D紋理是OpenGL ES 2.0規(guī)范中的可選項(xiàng)的一個(gè)原因。通常情況下內(nèi)存和顯存都是有限的婚苹,屏幕的尺寸也比較小岸更。這意味著使用一個(gè)更小尺寸的紋理能夠得到近似的視覺(jué)效果。避免多重采樣也是有必要的租副,因?yàn)樗鼤?huì)占用更多的存儲(chǔ)空間坐慰。
和紋理類(lèi)似,頂點(diǎn)存儲(chǔ)也會(huì)影響存儲(chǔ)空間用僧,處理為頂點(diǎn)數(shù)據(jù)使用內(nèi)存設(shè)置一個(gè)上限外结胀,確定場(chǎng)景中重要的部分,并在這些地方增加頂點(diǎn)的密度责循,其他地方相應(yīng)減少也是一個(gè)有效的辦法糟港。
當(dāng)在屏幕上渲染大量模型時(shí),一個(gè)保證平滑渲染的方式時(shí)根據(jù)模型和觀察者的距離院仿,改變模型的頂點(diǎn)數(shù)量秸抚。這是一個(gè)非常細(xì)節(jié)的幾何模型管理方式。例如歹垫,當(dāng)你需要渲染一片森林時(shí)剥汤,你可以使用三個(gè)不同頂點(diǎn)數(shù)量的模型。距離觀察者最遠(yuǎn)的樹(shù)使用最少頂點(diǎn)的模型渲染排惨,中間距離的樹(shù)使用中等數(shù)量頂點(diǎn)模型吭敢,而最近的樹(shù)使用最高頂點(diǎn)數(shù)量的模型。這樣那些離得比較遠(yuǎn)的樹(shù)渲染效率就會(huì)大幅提高暮芭,并且由于這些樹(shù)距離觀察者遠(yuǎn)鹿驼,因此它們通常僅能覆蓋很少的像素,因此使用較少頂點(diǎn)的模型看上去的細(xì)節(jié)差異也很難被注意到辕宏。然而我們需要處理的頂點(diǎn)數(shù)量卻大幅減小畜晰,這樣更有利于提升程序渲染性能。
定點(diǎn)數(shù)計(jì)算
OpenGL ES 3.0支持全浮點(diǎn)型數(shù)據(jù)瑞筐,但是一些更老的平臺(tái)原生并不支持浮點(diǎn)型數(shù)據(jù)凄鼻。浮點(diǎn)型數(shù)據(jù)計(jì)算在CPU模擬中非常慢,應(yīng)該避免這些操作聚假。在這些場(chǎng)景下块蚌,一種浮點(diǎn)型數(shù)據(jù)的表示方式坑過(guò)被用于和非完全數(shù)值集交互。即使你的處理器和GPU硬件理解完全浮點(diǎn)型數(shù)據(jù)魔策,利用定點(diǎn)數(shù)據(jù)使得數(shù)據(jù)存儲(chǔ)空間明顯減小匈子,數(shù)據(jù)加載速度更快河胎,以及其他優(yōu)勢(shì)闯袒。
浮點(diǎn)型數(shù)據(jù)由小數(shù)部分(m)和指數(shù)部分(e)組成,可以表示為m??2e,這種表示方式使得大的浮點(diǎn)數(shù)和小的浮點(diǎn)數(shù)具有相同的有效位政敢。而定點(diǎn)數(shù)的表示方式則不同其徙,它看上去更像是普通的整型數(shù)據(jù)。它的二進(jìn)制位被分為多個(gè)段喷户,最高位是符號(hào)位唾那,然后是整數(shù)位和小數(shù)位,如果一個(gè)s15.16格式的定點(diǎn)數(shù)表示由1個(gè)符號(hào)位褪尝,15個(gè)整數(shù)位和16個(gè)小數(shù)位組成闹获。這是OpenGL ES默認(rèn)的表示定點(diǎn)數(shù)格式。
兩個(gè)定點(diǎn)數(shù)的加法和減法運(yùn)算比較簡(jiǎn)單河哑,因?yàn)閷?duì)應(yīng)的整數(shù)部分和小數(shù)部分直接按標(biāo)量形式相加或相減即可避诽。但是需要注意的是,兩個(gè)定點(diǎn)數(shù)必須轉(zhuǎn)化到相同的格式才能進(jìn)行運(yùn)算璃谨。
定點(diǎn)數(shù)的乘法和除法運(yùn)算相對(duì)而言更復(fù)雜沙庐,當(dāng)它們相乘時(shí),小數(shù)部分所占用的位數(shù)是兩個(gè)定點(diǎn)數(shù)小數(shù)位之和佳吞,也就是說(shuō)兩個(gè)s23.8格式的定點(diǎn)數(shù)相乘拱雏,其值的格式為s15.16。因此在執(zhí)行乘法運(yùn)算之前最后將兩個(gè)定點(diǎn)數(shù)的格式轉(zhuǎn)換到具有合適的精度底扳。你可能并不會(huì)對(duì)兩個(gè)s15.16格式的定點(diǎn)數(shù)執(zhí)行乘法運(yùn)算铸抑,因?yàn)槿绻?dāng)其結(jié)果大于1時(shí),沒(méi)有任何空間能夠保存整數(shù)數(shù)據(jù)花盐。除法運(yùn)算也類(lèi)似羡滑,只是得到的值小數(shù)部分的位數(shù)是第一個(gè)和第二個(gè)定點(diǎn)數(shù)小數(shù)位數(shù)的差。
在使用定點(diǎn)數(shù)計(jì)算時(shí)算芯,你還需要特別留心溢出問(wèn)題柒昏。對(duì)于通常的浮點(diǎn)數(shù)而言,當(dāng)小數(shù)部分溢出時(shí)熙揍,指數(shù)部分會(huì)發(fā)生變化從而確保數(shù)據(jù)準(zhǔn)確性并防止溢出职祷。對(duì)于定點(diǎn)數(shù)而言并不存在這個(gè)機(jī)制。為了避免定點(diǎn)數(shù)計(jì)算時(shí)數(shù)據(jù)溢出届囚,可能你需要改變其數(shù)據(jù)格式有梆。對(duì)于乘法操作而言,當(dāng)定點(diǎn)數(shù)據(jù)被從一個(gè)格式轉(zhuǎn)換到另外一個(gè)格式時(shí)會(huì)存在小數(shù)部分精度丟失問(wèn)題意系。有很多數(shù)學(xué)庫(kù)可以幫助我們指定定點(diǎn)數(shù)運(yùn)算泥耀,如果你打算在整個(gè)程序中使用定點(diǎn)數(shù)計(jì)算,這是一個(gè)不錯(cuò)的選擇蛔添。
需要注意的是在OpenGL ES的著色器語(yǔ)言中痰催,對(duì)于所有數(shù)據(jù)類(lèi)型我們都必須聲明精度兜辞,可以聲明的精度有highp、mediump和lowp夸溶,每種數(shù)據(jù)類(lèi)型都有不同的精度和取值區(qū)間逸吵,可以參考OpenGL ES著色器語(yǔ)言的標(biāo)準(zhǔn)規(guī)范。對(duì)單個(gè)變量的精度聲明示例代碼缝裁。
lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;
你也可以定義每個(gè)數(shù)據(jù)類(lèi)型的默認(rèn)精度扫皱,示意代碼如下。
precision highp float;
precision highp int;
precision lowp sampler2D;
precision lowp samplerCube;
上面就是關(guān)于定點(diǎn)數(shù)學(xué)計(jì)算的基本知識(shí)捷绑,它們足以支持開(kāi)發(fā)簡(jiǎn)單的嵌入式OpenGL ES應(yīng)用韩脑。關(guān)于定點(diǎn)數(shù)學(xué)計(jì)算的更多細(xì)節(jié),可以參考Essential Mathematics for Games and Interactive Applications by James Van Verth and Lars Bishop (Elsevier, Inc., 2004)粹污。
3.4.6 EGL一個(gè)新的窗口環(huán)境
前面的文章中已經(jīng)簡(jiǎn)單介紹過(guò)分別應(yīng)用于Linux扰才、Windows和Mac OS系統(tǒng)中的GLX、AGL和WGL接口厕怜。要在對(duì)應(yīng)平臺(tái)上創(chuàng)建和管理OpenGL使用的系統(tǒng)資源需要使用相應(yīng)的接口衩匣,需要注意的是蘋(píng)果64位操作系統(tǒng)已經(jīng)不支持AGL。通常顯卡供應(yīng)商也提供EGL實(shí)現(xiàn)粥航,和其他窗口接口不同琅捏,EGL和平臺(tái)不相關(guān)。它被設(shè)計(jì)用于在Windows递雀、Linux或者如Android和iOS等嵌入式的系統(tǒng)中柄延。EGL和OpenGL在嵌入式系統(tǒng)中的交互關(guān)系如下。
EGL和OpenGL ES一樣有著自己的原生數(shù)據(jù)類(lèi)型缀程。EGLBoolean
也包含兩個(gè)對(duì)應(yīng)的值搜吧,分別是EGL_TRUE
和EGL_FALSE
。同樣的EGL定義了EGLint
類(lèi)型杨凑,該類(lèi)型和具體平臺(tái)上的整型大學(xué)相同滤奈。最新的EGL版本是EGL1.4。
EGL Displays
大多數(shù)OpenG的切入點(diǎn)都以EGLDisplay
作為參數(shù)撩满,它是對(duì)渲染靶點(diǎn)的引用蜒程。最簡(jiǎn)單的方式可以將其理解為一個(gè)物理顯示器。程序的第一步是通過(guò)如下函數(shù)設(shè)置EGL從而得到默認(rèn)顯示設(shè)備伺帘。
EGLDisplay eglGetDisplay(NativeDisplayType display_id);
上述函數(shù)的參數(shù)display_id
的傳值取決于具體的系統(tǒng)昭躺,如果你在Windows上使用EGL,該參數(shù)傳入設(shè)備上下文伪嫁。如果你并不知道display_id
但是你只是想在默認(rèn)的設(shè)備上繪制圖形领炫,你可以傳入常量EGL_DEFAULT_DISPLAY
。如果該函數(shù)的返回值是EGL_NO_DISPLAY
张咳,這意味著內(nèi)部發(fā)生了錯(cuò)誤帝洪。當(dāng)該函數(shù)成功執(zhí)行后就可以得到一個(gè)顯示句柄针史,你可以使用如下函數(shù)來(lái)初始化EGL。如果你在未初始化EGL之前就調(diào)用其相關(guān)的接口碟狞,那么將會(huì)得到一個(gè)標(biāo)識(shí)為EGL_NOT_INITIALIZED
的錯(cuò)誤。
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
參數(shù)major
和minor
是一個(gè)地址婚陪,當(dāng)前平臺(tái)支持的最低和最高EGL版本將會(huì)被寫(xiě)入到該地址中族沃。調(diào)用該初始化函數(shù)后,EGL就能夠創(chuàng)建和配置必要的資源泌参。EGL提供接口eglBindAPI()
用于應(yīng)用從如OpenGL脆淹、OpenGL ES和OpenVG中選擇需要使用的接口類(lèi)型,該函數(shù)參數(shù)需要從枚舉值EGL_OPENGL_API
沽一、EGL_OPENGL_ES_API
或者EGL_OPENVG_API
中選取盖溺。同樣,在每個(gè)線(xiàn)程中只能有1個(gè)激活的EGL上下文铣缠,因此接下來(lái)需要調(diào)用函數(shù)eglMakeCurrent()
指定當(dāng)前線(xiàn)程激活的上下文烘嘱。OpenVG是一個(gè)在較老的嵌入式設(shè)備中用于向量圖形運(yùn)算的API。綁定API類(lèi)型的函數(shù)原型如下蝗蛙。
EGLBoolean eglBindAPI(EGLenum api);
通過(guò)調(diào)用如下函數(shù)可以查詢(xún)當(dāng)前使用的API類(lèi)型蝇庭,其返回值是EGL_OPENGL_API
、EGL_OPENGL_ES_API
或者EGL_OPENVG_API
中的一個(gè)捡硅。
EGLenum eglQueryAPI(void);
在退出程序或者完成渲染工作后哮内,必須調(diào)用如下函數(shù)清空所有分配的資源。當(dāng)該函數(shù)調(diào)用后壮韭,在當(dāng)前顯示設(shè)置中對(duì)EGL資源的所有引用將會(huì)被標(biāo)記為無(wú)效北发,直到函數(shù)eglInitialize()
被再次調(diào)用辜御。
EGLBoolean eglTerminate(EGLDisplay dpy);
同樣的某個(gè)線(xiàn)程退出或者當(dāng)前線(xiàn)程中的渲染任務(wù)完成后闸翅,需要調(diào)用函數(shù)eglReleaseThread()
釋放在當(dāng)前線(xiàn)程上的所有資源。如果當(dāng)前線(xiàn)程有綁定的上下文弛随,該函數(shù)同樣會(huì)釋放這個(gè)上下文資源屯曹。在該函數(shù)之后从绘,你仍然可以正常調(diào)用EGL的資源,只是這樣會(huì)使EGL重新分配被釋放的資源是牢。
EGLBoolean \eglReleaseThread(EGLDisplay dpy);
有關(guān)EGL的細(xì)節(jié)這里就不再繼續(xù)覆蓋僵井,因?yàn)檫@不是本文的主要類(lèi)型,只需要再了解和OpenGL一樣驳棱,EGL同樣支持?jǐn)U展批什。
3.4.7 嵌入式環(huán)境
在簡(jiǎn)單介紹完OpenGL ES和EGL如何在嵌入式系統(tǒng)中工作后,下面會(huì)進(jìn)一步介紹嵌入式系統(tǒng)環(huán)境以及它是如何影響一個(gè)OpenGL ES應(yīng)用社搅。在創(chuàng)建ES程序的時(shí)候環(huán)境總扮演了一個(gè)重要的角色驻债。
流行的操作系統(tǒng)
OpenGL ES和大多數(shù)3D圖形API僅限于在特定的操作系統(tǒng)上不同乳规,它可以應(yīng)用在大量的操作系統(tǒng)中。現(xiàn)今最常見(jiàn)的兩個(gè)手機(jī)平臺(tái)是谷歌的Android和蘋(píng)果公司的iOS系統(tǒng)合呐。
特定供應(yīng)商的擴(kuò)展
每個(gè)OpenGL ES供應(yīng)商通常有一套僅僅限于它的硬件和實(shí)現(xiàn)的擴(kuò)展暮的,這些擴(kuò)展通常增加了可用格式的數(shù)量和類(lèi)型。由于它們僅僅用于有限的硬件集合淌实,這里不深入介紹冻辩。
模擬器的ES程序
如果你無(wú)法在嵌入式環(huán)境中開(kāi)發(fā)ES程序,你仍可以使用一些其他方式在桌面操作系統(tǒng)中開(kāi)發(fā)ES程序拆祈,使用這些OpenGL ES的實(shí)現(xiàn)來(lái)做簡(jiǎn)單圖形程序開(kāi)發(fā)也是一個(gè)不錯(cuò)的主意恨闪。英偉達(dá)和AMD公司都提供一些方式使得我們可以在使用獨(dú)立顯卡的桌面電腦中創(chuàng)建創(chuàng)建ES配置,你可以使用這些配置創(chuàng)建OpenGL ES 2.0或者3.0的應(yīng)用放坏。另外在谷歌和蘋(píng)果的平臺(tái)上開(kāi)發(fā)OpenGL ES的程序都十分容易咙咽。
安卓平臺(tái)
安卓系統(tǒng)如今占據(jù)了智能手機(jī)市場(chǎng)超過(guò)百分之五十的份額。當(dāng)你在安卓應(yīng)用市場(chǎng)上發(fā)布程序時(shí)淤年,這意味著將有上千萬(wàn)的手機(jī)或者平板電腦設(shè)備能夠看到你的應(yīng)用钧敞。因?yàn)榘沧繎?yīng)用運(yùn)行的設(shè)備來(lái)自于很多制造商,因此你需要記住你的應(yīng)用需要面對(duì)不同能力和性能的硬件條件麸粮。在安卓2.2及其以后犁享,安卓系統(tǒng)支持OpenGL ES 1.1和ES 2.0,后來(lái)安卓也繼續(xù)支持了ES 3.0豹休。
谷歌提供了很多如何在安卓系統(tǒng)中開(kāi)發(fā)OpenGL ES應(yīng)用的教程炊昆,但是這部分內(nèi)容超出了本文探討的范疇,它們可以在其官方網(wǎng)站上找到威根。開(kāi)發(fā)安卓應(yīng)用以及在自己的設(shè)備上運(yùn)行都是免費(fèi)的凤巨,但是當(dāng)你將其部署在官方的安卓商店中需要支付少量的費(fèi)用。
安卓開(kāi)發(fā)環(huán)境
谷歌提供了很多示例程序幫助開(kāi)發(fā)者快速熟悉安卓操作系統(tǒng)的很多特性洛搀,總的來(lái)說(shuō)谷歌提供了原生開(kāi)發(fā)庫(kù)(Native Development Kit, NDK)和軟件開(kāi)發(fā)庫(kù)(Software Development Kit, SDK)兩套開(kāi)發(fā)框架供我們使用敢茁。
使用NDK開(kāi)發(fā)應(yīng)用可以使用C和C++語(yǔ)言,該庫(kù)對(duì)于遷移已有的應(yīng)用到安卓平臺(tái)特別有用留美。對(duì)于大型游戲引擎彰檬,特別是不使用Java庫(kù)的應(yīng)用而言,這是一個(gè)理想的方式谎砾。另外逢倍,使用該庫(kù)也能獲得更好的性能。同樣的你也可以更精細(xì)的控制窗口系統(tǒng)以及EGL環(huán)境從而使它們更匹配當(dāng)前應(yīng)用景图。但是较雕,使用NDK也能夠使得代碼更復(fù)雜,從而影響應(yīng)用的移植性。
SDK提供了更簡(jiǎn)單的API亮蒋,使得程序的開(kāi)發(fā)和調(diào)試都更容易扣典,對(duì)于新手而言這種方式是一個(gè)理想的選擇。另外它包含一些工具和組建使我們可以在Eclipse集成開(kāi)發(fā)環(huán)境中開(kāi)發(fā)應(yīng)用慎玖。SDK會(huì)處理大多數(shù)管理EGL和設(shè)置GL環(huán)境的細(xì)節(jié)贮尖,我們并不需要直接調(diào)用OpenGL ES中的API,直接調(diào)用Java橋接層的接口即可趁怔。原書(shū)中使用這種方式介紹了如何在Android環(huán)境中開(kāi)發(fā)應(yīng)用湿硝,并給出了相關(guān)的例子,但是這暫不在本文的探討范疇內(nèi)痕钢。
iOS開(kāi)發(fā)環(huán)境
蘋(píng)果公司的iOS系統(tǒng)在早期運(yùn)行在其主流的設(shè)備iPhone、iPod Touch和iPad之上序六,盡管如今iPad有了獨(dú)立的系統(tǒng)Pad OS任连,但是這幾種設(shè)備的最新型號(hào)都支持OpenGL ES 3.0。另外iOS系統(tǒng)還運(yùn)行在iWatch例诀,iTV和iMicrowaves之上随抠。
本系列博客的第一篇就對(duì)OpenGL ES的iOS開(kāi)發(fā)環(huán)境做了介紹,并且給出了示例程序繁涂,因此這里不再重復(fù)拱她。本書(shū)除了少量的示例程序運(yùn)行在iOS系統(tǒng)上,剩余的大部分示例程序都運(yùn)行在mac OS之上扔罪。
iOS原生編程環(huán)境使用的語(yǔ)言是Objective-C秉沼,而大多數(shù)的非蘋(píng)果編程者使用的都是C或者C++語(yǔ)言。但是如果你想要使用蘋(píng)果提供的系統(tǒng)庫(kù)矿酵,那么必須熟悉Objective-C唬复。Objective-C可以簡(jiǎn)單的理解為面向?qū)ο蟮腃語(yǔ)言,但是它和C++又不同全肮。將一個(gè)文件的后綴由m改為mm可以將該文件設(shè)置為C++和OC混編模式敞咧,即可以在當(dāng)前文件中使用兩種語(yǔ)言開(kāi)發(fā)。另外需要注意的是辜腺,蘋(píng)果自己目前已經(jīng)全面采用自家的Swift語(yǔ)言休建,并且推薦開(kāi)發(fā)者也使用該語(yǔ)言。Swift語(yǔ)言比OC更安全和高效评疗,但是具體細(xì)節(jié)超出了本文的討論范圍测砂,可以參考官方文檔。
在使用OpenGL ES 2.0的時(shí)百匆,需要注意部分功能并不被核心版本支持邑彪,但是蘋(píng)果提供擴(kuò)展使得我們可以正常使用這些功能,并切這些擴(kuò)展也被OpenGL工作組定義胧华,并被包含在其后續(xù)版本ES 3.0中寄症。以擴(kuò)展形式使用這些功能時(shí)需要注意加上OES后綴宙彪,它們包括如下功能。
public func glGenVertexArraysOES(_ n: GLsizei, _ arrays: UnsafeMutablePointer<GLuint>!)
public func glBindVertexArrayOES(_ array: GLuint)
public func glDeleteVertexArraysOES(_ n: GLsizei, _ arrays: UnsafePointer<GLuint>!)
public func glUnmapBufferOES(_ target: GLenum) -> GLboolean
public func glMapBufferOES(_ target: GLenum, _ access: GLenum) -> UnsafeMutableRawPointer!
后兩個(gè)是操作頂點(diǎn)數(shù)組對(duì)象的擴(kuò)展有巧,而第四個(gè)函數(shù)是映射緩存地址的擴(kuò)展函數(shù)释漆。需要注意的是函數(shù)glMapBufferOES()
的參數(shù)access
參數(shù)只能傳入GL_WRITE_ONLY
。
OpenGL ES 2.0使用的著色器語(yǔ)言基于OpenGL 2.0修改篮迎,盡管ES 3.0要求在著色源碼的前面加上指定當(dāng)前著色器版本的修飾符#version 300 ES
男图,但在ES 2.0使用的著色器語(yǔ)言中并不能直接識(shí)別這條預(yù)編譯指令。在對(duì)OpenGL所使用的著色器語(yǔ)言有了解后甜橱,再熟悉其和ES使用的著色器語(yǔ)言的差異逊笆,就能很快開(kāi)始編寫(xiě)ES的著色器。
在ES 2.0中不能使用關(guān)鍵字in
和out
岂傲,ES 2.0所使用的GLSL是基于只存在頂點(diǎn)著色器和片段著色器的雙著色器器系統(tǒng)設(shè)計(jì)的难裆,在頂點(diǎn)著色器中所有的頂點(diǎn)屬性都使用關(guān)鍵字attribute
聲明如下。
attribute vec4 vVertexPos;
頂點(diǎn)著色器輸出的值對(duì)經(jīng)過(guò)插值計(jì)算被傳入到片段著色器中镊掖,在這兩個(gè)著色器中這些變量都使用關(guān)鍵字varying
聲明如下乃戈。另外對(duì)于插值計(jì)算方式控制的關(guān)鍵字smooth
和centroid
也被支持。
varying vec2 vTexCoordVary;
在片段著色器中亩进,正如在頂點(diǎn)著色器中存在內(nèi)建變量gl_Position用于指定頂點(diǎn)的輸出數(shù)量症虑,在片段著色器中存在內(nèi)建變量gl_FragColor用于指定片段的最終顏色。另外归薛,你可以使用如下方式進(jìn)行紋理采樣并計(jì)算片段顏色谍憔。
gl_FragColor = texture2D(colorMap, vTexCoordVary.st);
在桌面版本的GLSL中現(xiàn)在也支持?jǐn)?shù)據(jù)精度修飾符,而在ES 2.0后必須為片段著色器中的浮點(diǎn)數(shù)據(jù)設(shè)置精度主籍。在前面的小節(jié)中已經(jīng)介紹過(guò)如何聲明數(shù)據(jù)精度韵卤,這里不再累述。需要注意通常情況下medium聲明的精度最夠應(yīng)對(duì)浮點(diǎn)數(shù)據(jù)計(jì)算崇猫,特別是對(duì)于顏色數(shù)據(jù)的計(jì)算沈条,使用high聲明默認(rèn)浮點(diǎn)精度可能會(huì)耗費(fèi)大量計(jì)算資源降低程序性能。
精度聲明的關(guān)鍵字僅是對(duì)OpenGL ES實(shí)現(xiàn)的一個(gè)暗示诅炉,暗示被聲明變量的用途蜡歹,大多數(shù)iOS設(shè)備使用的PowerVR顯卡能夠利用這些關(guān)鍵字,并且小心的適配從而得到很好的性能收益涕烧。
4 其他資料
本系列文章到這里已經(jīng)全面的覆蓋了OpenGL 4.1核心版本的所有知識(shí)月而。實(shí)時(shí)3D圖形學(xué)和OpenGL是一個(gè)很流行的話(huà)題,還有很多資料可以加強(qiáng)我們?cè)谶@個(gè)方向的知識(shí)積累和技術(shù)深度议纯。
4.1 OpenGL書(shū)籍
- McReynolds, T., and Blythe, D. (2005). Advanced Graphics Programming Using OpenGL. Morgan Kaufmann.
- Angel, E., and Shreiner, D. (2011). Interactive Computer Graphics: A Top-Down Approach with Shader-Based OpenGL (6th Edition). Addison-Wesley.
- Astle, D. (ed.) (2006). More OpenGL Game Programming. Thomson Course Technology.
- Munshi, A., Ginsburg, D., and Shreiner, D. (2008). OpenGL ES 2.0 Programming Guide. Addison-Wesley.
- Shreiner, D., Sellers, G., Kessenich, J., and Licea-Kane, B. (2013). OpenGL Programming Guide, 8th Edition: The Official Guide to Learning OpenGL, Version 4.3. Addison-Wesley.
- Cozzi, P., and Riccio, C. (eds.) (2012). OpenGL Insights. CRC Press.
- Wolff, D. (ed.) (2011). OpenGL 4.0 Shading Language Cookbook. Packt Publishing.
4.2 3D圖形學(xué)書(shū)籍
- Watt, A. (1999). 3D Computer Graphics, 3rd Edition. Addison-Wesley.
- Dunn, F., and Parberry, I. (2011). 3D Math Primer for Graphics and Game
Development, 2nd Edition. A.K. Peters / CRC Press. - Van Verth, J., and Bishop, L. (2008). Essential Mathematics for Games and
Interactive Applications, 2nd Edition. Morgan Kaufmann. - Foley, J. D., et al. (1993). Introduction to Computer Graphics.
Addison-Wesley. - Lengyel, E. (2011). Mathematics for 3D Game Programming & Computer
Graphics, 3rd Edition. Course Technology PTR. - Akenine-Moller, T., Haines, E., and Hoffman, N. (2008). Real-Time
Rendering, 3rd Edition. A.K. Peters. - Engel, W. (ed.) (2006). Shader X 4: Advanced Rendering Techniques. Charles
River Media.
4.3 網(wǎng)站和論壇
- The OpenGL? SuperBible, Sixth Edition, Web site
- The official OpenGL Web site
- The OpenGL SDK (lots of tutorials and tools)
上面三個(gè)網(wǎng)站是學(xué)習(xí)OpenGL的門(mén)戶(hù)網(wǎng)站父款,也是餿油OpenGL和OpenGL SuperBible資料的官方網(wǎng)站。下面的這些網(wǎng)站也適用于本系列博客中的知識(shí),在這些網(wǎng)站中包含特定供應(yīng)商的OpenGL教程憨攒,示例程序和新特性世杀。
- The Khronos Group OpenGL ES home page
- The OpenGL Extension Registry
- AMD’s developer home page
- NVIDIA’s developer home page
- The Mesa 3D OpenGL “work-alike”
- GLView OpenGL Extension Viewer
下面是一個(gè)在線(xiàn)著色器調(diào)試的網(wǎng)站,我們可以從中找到一些高級(jí)特效的示例代碼肝集,也可以在線(xiàn)調(diào)試我們自己的著色器代碼瞻坝。
5 總結(jié)
本章介紹了如何在Windows中使用Win32 API、在Mac OS X中使用Cocoa杏瞻、以及在Linux中使用X系統(tǒng)開(kāi)發(fā)OpenGL程序所刀。OpenGL是開(kāi)發(fā)Mac應(yīng)用的核心框架,對(duì)OpenGL有基礎(chǔ)的了解捞挥,并且知道應(yīng)用和OpenGL之間如何交互是一個(gè)Mac OS X開(kāi)發(fā)者的基本技能浮创。另外在Linux平臺(tái)上,通常OpenGL是唯一可用的3D圖形API砌函。
在所有的平臺(tái)中斩披,我們已經(jīng)介紹了如何查找最適合當(dāng)前渲染需求的配置,也講到了如何創(chuàng)建特定OpenGL版本的上下文胸嘴。另外本文也介紹了如何在程序結(jié)束時(shí)情況窗口系統(tǒng)的狀態(tài)并釋放OpenGL的資源雏掠。
最后我們接觸了OpenGL的嵌入式版本OpenGL ES斩祭,它幾乎統(tǒng)治了所有的移動(dòng)平臺(tái)圖形處理API劣像。
6 結(jié)語(yǔ)
到這里歷時(shí)一年多的OpenGL系列也正式結(jié)束,本系列博客僅是全面講解了基礎(chǔ)知識(shí)摧玫,要成長(zhǎng)為一個(gè)優(yōu)秀的圖形處理方向工程師甚至專(zhuān)家的路還很長(zhǎng)耳奕。最后希望能夠和各位有興趣的小伙伴,大牛??交流學(xué)習(xí)诬像。謹(jǐn)用一句話(huà)送給自己以及和我一樣希望在音視頻處理屋群、圖形識(shí)別和處理方向逆風(fēng)而行的小伙伴。我們的征途是星辰大海坏挠。