天天看點

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

本節書摘來自華章出版社《opengl es應用開發實踐指南:android卷》一 書中的第3章,第3.2節,作者:(美)kevin brothaler ,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。

現在我們已經把着色器源代碼從檔案中讀出來了,下一步就是編譯每個着色器了。我們要建立一個新的輔助類,它可以建立新的opengl着色器對象、編譯着色器代碼并且傳回代表那段着色器代碼的着色器對象。一旦寫出樣闆代碼,在未來的項目中就可以重用了。

作為開始,建立一個名為shaderhelper的新類,并在類中添加如下代碼:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器
《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

這些代碼會作為着色器輔助類的基礎。與以前一樣,不要忘了把導入加進代碼中;如果你在使用靜态導入時碰到什麼問題,請參考1.5節;在本書的剩餘部分,我們會一直遵循這個樣式。

在下一節中,我們會逐漸建構compileshader()方法。

我們應該做的第一件事就是建立一個新的着色器對象,并且檢查這個建立是否成功。在compileshader()中加入如下代碼:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

這裡,用glcreateshader()調用建立了一個新的着色器對象,并把這個對象的id存入變量shaderobjectid。這個type可以是代表頂點着色器的gl_vertex_shader,或者是代表片段着色器的gl_fragment_shader。剩下的代碼也用同樣的方式。

記住我們是如何建立對象并檢查它是否有效的;這個模式将在opengl裡廣泛使用:

1.首先使用一個如glcreateshader()一樣的調用建立一個對象,這個調用會傳回一個整型值(integer)。

2.這個整型值就是opengl對象的引用。無論後面什麼時候想要引用這個對象,就要把這個整型值傳回opengl。

3.傳回值0表示這個對象建立失敗,它類似于java代碼中傳回null值。

如果對象建立失敗,就給調用代碼傳回0。為什麼傳回0而不是抛出一個異常呢?這是因為opengl内部實際不會抛出任何異常;相反,我們會得到傳回值0,并且opengl通過glgeterror()告訴我們這個錯誤,這個方法可以讓我們詢問opengl是不是某個api調用導緻了錯誤。我們會一直遵從這個慣例。

附錄b介紹了更多關于glgeterror()的知識以及其他調試opengl代碼的方法。

讓我們加入如下代碼把着色器源代碼上傳到着色器對象裡:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

一旦有了有效的着色器對象,就可以調用glshadersource(shaderobjectid, shadercode)上傳源代碼了。這個調用告訴opengl讀入字元串shadercode定義的源代碼,并把它與shaderobjectid所引用的着色器對象關聯起來。然後,可以調用glcompileshader(shaderobjectid)編譯這個着色器:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

這個調用告訴opengl編譯先前上傳到shaderobjectid的源代碼。

讓我們加入如下代碼檢查opengl是否能成功地編譯這個着色器:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

為了檢查編譯是失敗還是成功,首先要建立一個新的長度為1的int數組,稱為compilestatus;然後調用glgetshaderiv(shaderobjectid, gles20.gl_compile_status, compilestatus, 0)。這就告訴opengl讀取與shaderobjectid關聯的編譯狀态,并把它寫入compilestatus的第0個元素。

這是android平台上的opengl的另外一個通用模式。為了取出一個值,我們通常會使用一個長度為1的數組,并把這個數組傳進一個opengl調用。在同一個調用中,我們告訴opengl把結果存進數組的第一個元素中。

當我們獲得編譯狀态的時候,opengl隻給出一個簡單的是或否的回答。難道沒興趣知道發生了什麼錯誤以及哪裡出問題了嗎?事實證明,我們可以通過調用glgetshaderinfolog(shaderobjectid)獲得一個可讀的消息。如果opengl有什麼關于着色器的有用内容,它就會把消息存到着色器的資訊日志裡。

讓我們加入如下代碼擷取着色器資訊日志:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

我們把日志輸出到android的日志輸出中,并把一切都封裝在那個檢查loggerconfg.on值的if語句裡。通過把這個常量指派為“false”,我們可以很容易地關閉這些日志。

既然我們已經記錄了着色器資訊日志,就可以檢視一下編譯是否成功了:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

我們所需要做的就是檢查在3.2.3節那步的傳回值,它是不是0。如果它是0,編譯就失敗了,這種情況下,我們就不再需要着色器對象了,是以告訴opengl把它删除并傳回0給調用代碼;如果編譯成功,着色器對象就是有效的,我們就可以在代碼中使用它了。

這就是為了編譯一個着色器所需要的全部内容,讓我們傳回那個新的着色器對象id:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

現在是時候充分利用我們剛剛寫過的代碼了。切換到airhockeyrender.java,并在onsurfacecreated()的結尾處加入如下代碼:

《OpenGL ES應用開發實踐指南:Android卷》—— 3.2 編譯着色器

讓我們回顧一下本節所完成的工作。首先,我們建立了一個新類shaderhelper,并加入了一個用來建立、編譯新着色器對象的方法;我們也建立了loggerconfig,一個用來在單一代碼行打開或者關閉日志的類。

如果你再看一下shaderhelper,就會發現我們實際上定義了三個方法。

compileshader():這個compileshader(type, shadercode)方法使用了着色器源代碼和類型;type可以是代表頂點着色器的gl_vertex_shader,或者是代表片段着色器的gl_fragment_shader。如果opengl能成功編譯這個着色器,這個方法就會給調用代碼傳回着色器對象的id,否則,它就會傳回0。

compilevertexshader():這個compilevertexshader(shadercode)方法是調用compileshader()的輔助方法,使用gl_vertex_shader作為着色器類型。

compilefragmentshader():這個compilevertexshder(shadercode)方法也是調用compileshader()的輔助方法,它使用gl_fragment_shader作為着色器類型。

如你所見,這段代碼的實質内容都在compileshader()中;其他兩個方法就是使用gl_vertex_shader或gl_fragment_shader調用它。

繼續閱讀