在 OpenGL ES 中你必須創(chuàng)建兩種著色器:頂點(diǎn)著色器 (vertex shaders) 和片段著色器 (fragment shaders)。
相關(guān)概念
VertexShader 頂點(diǎn)著色器
FragmentShader 片段著色器
頂點(diǎn)著色器定義了在 2D 或者 3D 場(chǎng)景中幾何圖形是如何處理的。一個(gè)頂點(diǎn)指的是 2D 或者 3D 空間中的一個(gè)點(diǎn)。在圖像處理中,有 4 個(gè)頂點(diǎn):每一個(gè)頂點(diǎn)代表圖像的一個(gè)角。頂點(diǎn)著色器設(shè)置頂點(diǎn)的位置,并且把位置和紋理坐標(biāo)這樣的參數(shù)發(fā)送到片段著色器。
然后 GPU 使用片段著色器在對(duì)象或者圖片的每一個(gè)像素上進(jìn)行計(jì)算,最終計(jì)算出每個(gè)像素的最終顏色。圖片,歸根結(jié)底,實(shí)際上僅僅是數(shù)據(jù)的集合。圖片的文檔包含每一個(gè)像素的各個(gè)顏色分量和像素透明度的值。因?yàn)閷?duì)每一個(gè)像素,算式是相同的,GPU 可以流水線作業(yè)這個(gè)過程,從而更加有效的進(jìn)行處理。使用正確優(yōu)化過的著色器,在 GPU 上進(jìn)行處理,將使你獲得百倍于在 CPU 上用同樣的過程進(jìn)行圖像處理的效率。
把東西渲染到屏幕上從一開始就是一個(gè)困擾 OpenGL 開發(fā)者的問題。僅僅讓屏幕呈現(xiàn)出非黑色就要寫很多樣板代碼和設(shè)置。開發(fā)者必須跳過很多坑 ,而這些坑所帶來的沮喪感以及著色器測(cè)試方法的匱乏,讓很多人放棄了哪怕是嘗試著寫著色器。
Uniforms 是一種外界和你的著色器交流的方式。Uniforms 是為在一個(gè)渲染循環(huán)里不變的輸入值設(shè)計(jì)的。如果你正在應(yīng)用茶色濾鏡,并且你已經(jīng)指定了濾鏡的強(qiáng)度,那么這些就是在渲染過程中不需要改變的事情,你可以把它作為 Uniform 輸入。 Uniform 在頂點(diǎn)著色器和片段著色器里都可以被訪問到。
Attributes 僅僅可以在頂點(diǎn)著色器中被訪問。Attribute 是在隨著每一個(gè)頂點(diǎn)不同而會(huì)發(fā)生變動(dòng)的輸入值,例如頂點(diǎn)的位置和紋理坐標(biāo)等。頂點(diǎn)著色器利用這些變量來計(jì)算位置,以它們?yōu)榛A(chǔ)計(jì)算一些值,然后把這些值以 varyings 的方式傳到片段著色器。
最后,但同樣重要的,是 varyings 標(biāo)簽。Varying 在頂點(diǎn)著色器和片段著色器都會(huì)出現(xiàn)。Varying 是用來在頂點(diǎn)著色器和片段著色器傳遞信息的,并且在頂點(diǎn)著色器和片段著色器中必須有匹配的名字。數(shù)值在頂點(diǎn)著色器被寫入到 varying ,然后在片段著色器被讀出。被寫入 varying 中的值,在片段著色器中會(huì)被以插值的形式插入到兩個(gè)頂點(diǎn)直接的各個(gè)像素中去。
可編程管線
1.VBO/VAO(頂點(diǎn)緩沖區(qū)對(duì)象或頂點(diǎn)數(shù)組對(duì)象):
VBO/VAO是cpu提供給GPU的頂點(diǎn)信息,包括了頂點(diǎn)的位置、顏色(只是頂點(diǎn)的顏色,和紋理的顏色無關(guān))、紋理坐標(biāo)(用于紋理貼圖)等頂點(diǎn)信息。
2.VertexShader(頂點(diǎn)著色器):
頂點(diǎn)著色器是處理VBO/VAO提供的頂點(diǎn)信息的程序。VBO/VAO提供的每個(gè)頂點(diǎn)都執(zhí)行一遍頂點(diǎn)著色器。Uniforms(一種變量類型)在每個(gè)頂點(diǎn)保持一致,Attribute每個(gè)頂點(diǎn)都不同(可以理解為輸入頂點(diǎn)屬性)。執(zhí)行一次VertexShader輸出一個(gè)Varying和gl_positon。
3.PrimitiveAssembly(圖元裝配):
頂點(diǎn)著色器下一個(gè)階段是圖元裝配,圖元(prmitive)是三角形、直線或者點(diǎn)精靈等幾何對(duì)象。這個(gè)階段,把頂點(diǎn)著色器輸出的頂點(diǎn)組合成圖元。
4.rasterization(光柵化):
光柵化是將圖元轉(zhuǎn)化為一組二維片段的過程,然后,這些片段由片段著色器處理(片段著色器的輸入)。這些二維片段代表著可在屏幕上繪制的像素。用于從分配給每個(gè)圖元頂點(diǎn)的頂點(diǎn)著色器輸出生成每個(gè)片段值的機(jī)制稱作插值(Interpolation)。這句不是人話的話解釋了一個(gè)問題,就是從cpu提供的分散的頂點(diǎn)信息是如何變成屏幕上密集的像素的,圖元裝配后頂點(diǎn)可以理解成變?yōu)閳D形,光柵化時(shí)可以根據(jù)圖形的形狀,插值出那個(gè)圖形區(qū)域的像素(紋理坐標(biāo)v_texCoord、顏色等信息)。注意,此時(shí)的像素并不是屏幕上的像素,是不帶有顏色的。接下來的片段著色器完成上色的工作。
5.FragmentShader(片段著色器):
片段著色器為片段(像素)上的操作實(shí)現(xiàn)了通用的可編程方法,光柵化輸出的每個(gè)片段都執(zhí)行一遍片段著色器,對(duì)光柵化階段生成每個(gè)片段執(zhí)行這個(gè)著色器,生成一個(gè)或多個(gè)(多重渲染)顏色值作為輸出。
6.Per-Fragment Operations(逐片段操作)
在此階段,每個(gè)片段上執(zhí)行如下功能:
1)pixelOwnershipTest(像素歸屬測(cè)試):
這個(gè)用來確定幀緩沖區(qū)中位置(x,y)的像素是不是歸當(dāng)前上下文所有。例如,如果一個(gè)顯示幀緩沖區(qū)窗口被另一個(gè)窗口所遮蔽,則窗口系統(tǒng)可以確定被遮蔽的像素不屬于此opengl的上下文,從而不顯示這些像素。
(2)ScissorTest(剪裁測(cè)試):
如果該片段位于剪裁區(qū)域外,則被拋棄
(3)StencilTest and DepthTest(模板和深度測(cè)試):
深度測(cè)試比較好理解,若片段著色器返回的深度小于緩沖區(qū)中的深度,則舍棄。模板測(cè)試沒有用過,不清楚具體功能,猜測(cè)功能應(yīng)該和名字一樣,模板形狀內(nèi)可通過。
(4)Blending(混合):
將新生成的片段顏色值與保存在幀緩沖區(qū)的顏色值組合起來,產(chǎn)生新的RGBA。
(5)dithering(抖動(dòng)):
最后把產(chǎn)生的片段放到幀緩沖區(qū)(前緩沖區(qū)或后緩沖區(qū)或FBO)中,若不是FBO,則屏幕繪制緩沖區(qū)中的片段,產(chǎn)生屏幕上的像素。