OpenGL-結(jié)語(yǔ)

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_TRUEEGL_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ù)majorminor是一個(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_APIEGL_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)鍵字inout岂傲,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)鍵字smoothcentroid也被支持。

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)站和論壇

上面三個(gè)網(wǎng)站是學(xué)習(xí)OpenGL的門(mén)戶(hù)網(wǎng)站父款,也是餿油OpenGL和OpenGL SuperBible資料的官方網(wǎng)站。下面的這些網(wǎng)站也適用于本系列博客中的知識(shí),在這些網(wǎng)站中包含特定供應(yīng)商的OpenGL教程憨攒,示例程序和新特性世杀。

下面是一個(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)而行的小伙伴。我們的征途是星辰大海坏挠。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芍躏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子降狠,更是在濱河造成了極大的恐慌对竣,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,835評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榜配,死亡現(xiàn)場(chǎng)離奇詭異否纬,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蛋褥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)临燃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事膜廊》Ψ校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,481評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵溃论,是天一觀的道長(zhǎng)屎蜓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)钥勋,這世上最難降的妖魔是什么炬转? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,303評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮算灸,結(jié)果婚禮上扼劈,老公的妹妹穿的比我還像新娘。我一直安慰自己菲驴,他們只是感情好荐吵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,375評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著赊瞬,像睡著了一般先煎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巧涧,一...
    開(kāi)封第一講書(shū)人閱讀 49,729評(píng)論 1 289
  • 那天薯蝎,我揣著相機(jī)與錄音,去河邊找鬼谤绳。 笑死占锯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缩筛。 我是一名探鬼主播消略,決...
    沈念sama閱讀 38,877評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瞎抛!你這毒婦竟也來(lái)了艺演?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,633評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桐臊,失蹤者是張志新(化名)和其女友劉穎胎撤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體豪硅,經(jīng)...
    沈念sama閱讀 44,088評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哩照,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,443評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了懒浮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飘弧。...
    茶點(diǎn)故事閱讀 38,563評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡识藤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出次伶,到底是詐尸還是另有隱情痴昧,我是刑警寧澤,帶...
    沈念sama閱讀 34,251評(píng)論 4 328
  • 正文 年R本政府宣布冠王,位于F島的核電站赶撰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏柱彻。R本人自食惡果不足惜豪娜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,827評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哟楷。 院中可真熱鬧瘤载,春花似錦、人聲如沸卖擅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,712評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惩阶。三九已至挎狸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間断楷,已是汗流浹背锨匆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,943評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脐嫂,地道東北人统刮。 一個(gè)月前我還...
    沈念sama閱讀 46,240評(píng)論 2 360
  • 正文 我出身青樓紊遵,卻偏偏與公主長(zhǎng)得像账千,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子暗膜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,435評(píng)論 2 348

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