opengl es 着色器語言

   所有變量和函數在使用前必須聲明。變量和函數名是標識符。

       沒有默認類型,所有變量和函數聲明必須包含一個聲明類型以及可選的修飾符。變量在聲明的時候首先要標明類型,後邊可以跟多個變量,之間用逗號隔開。很多情況下,變量在聲明的時候可以使用等號“=”進行初始化。

       用戶定義類型可以使用struct,在結構體中所有變量類型都必須是OpenGL ES着色器語言定義的關鍵字。OpenGL ES着色語言是類型安全的,因此不支持隱式類型轉換。

4.1 基本數據類型


4.1.1  void

        函數沒有返回值必須聲明爲void,沒有默認的函數返回值。關鍵字void不能用於其他聲明,除了空形參列表外。

4.1.2  Booleans

       布爾值,只有兩個取值true或false。

           bool success, done = false;

4.1.3  Integers

       整型主要作爲編程的援助角色。在硬件級別上,真正地整數幫助有效的實現循環和數組索引,紋理單元索引。然而,着色語言沒必要將整型數映射到硬件級別的整數。我們並不贊成底層硬件充分支持範圍廣泛的整數操作。OpenGL ES着色語言會把整數轉化成浮點數進行操作。整型數可以使用十進制(非0開頭數字),八進制(0開頭數組)和十六進制表示(0x開頭數字)。

            int i, j = 42;

4.1.4  Floats

       浮點型用於廣泛的標量計算。可以如下定義一個浮點數:

           float a, b = 1.5;

4.1.5  Vectors

       OpenGL ES着色語言包含像2-,3-, 4-浮點數、整數、booleans型向量的泛型表示法。浮點型向量可以保存各種有用的圖形數據,如顏色,位置,紋理座標。

           vec2 texCoord1, texCoord2;

           vec3 position;

           vec4 rgba;

           ivec2 textureLookup;

           bvec3 lessThan;

        向量的初始化工作可以放在構造函數中完成。

4.1.6  Matrices

        矩陣是另一個在計算機圖形中非常有用的數據類型,OpenGL ES着色語言支持2*2, 3*3, 4*4浮點數矩陣。

           mat2 mat2D;

           mat3 optMatrix;

           mat4 view, projection;

        矩陣的初始化工作可以放在構造函數中完成。

4.1.7  Sampler

        採樣器類型(如sampler2D)實際上是紋理的不透明句柄。它們用在內建的紋理函數來指明要訪問哪一個紋理。它們只能被聲明爲函數參數或uniforms。除了紋理查找函數參數, 數組索引, 結構體字段選擇和圓括號外,取樣器不允許出現在表達式中。取樣器不能作爲左值,也不能作爲函數的out或inout參數。這些限制同樣適用於包含取樣器的的結構體。作爲uniforms時,它們通過OpenGL ES API初始化。作爲函數參數,僅能傳入匹配的採樣器類型。這樣可以在着色器運行之前進行着色器紋理訪問和OpenGL ES紋理狀態的一致性檢查。

4.1.8 Structures

         通過結構體用戶可以創建自己的數據類型。

               struct light{

                        float intensity;

                        vec3 position;

               }lightVar;

         結構體不支持內部匿名結構體對象,也不支持內部嵌入結構體,但可以聲明另一個結構體的變量。


4.1.9  Arrays

        同種類型的變量可以放在一個數組中保存和管理。數組長度必須是大於0的常整型數。用負數或大於等於數組程度的索引值索引數組是不合法的。數組作爲函數形參必須同時指明數組長度。僅支持一維數組,基本數據類型和結構體類型都可以作爲數組元素。

                float frequencies[3];

                uniform vec4 lightPosition[4];

                const int numLights = 2;

                light lights[bumLights];

        不能在着色器中聲明數組的同時進行初始化。

4.2  Scopes

         聲明的範圍決定了變量的可見性。GLSL ES使用了靜態嵌套範圍,允許在着色器內重定義一個變量。

4.2.1術語的定義

        術語scope說明程序的一個特定的區域,在這個區域定義的變量時可見的。

4.2.2範圍類型



4.2.3  重聲明變量

         在一個編譯單元,具有相同名字的變量不能重聲明在同一個範圍。可以在不同的範圍內聲明同名的變量,但沒有辦法訪問外層範圍的同名變量。

4.2.4共享全局變量

         共享全局變量是指可以在多個編譯單元訪問的變量。在GLSL ES中僅uniform變量可以作爲全局共享變量。varying變量不能作爲全局共享變量因爲在片元着色器能讀取它們之前必須通過光柵化傳遞。

         共享全局變量必須有相同的名字, 存儲和精度修飾符。它們必須有如下相同等價的規則:必須具有相同的精度,基本類型和尺寸。標量必須有一樣的類型名稱和類型定義,而且字段名稱必須爲相同類型。

4.3存儲修飾符

本地變量只能使用存儲修飾符const。

         函數參數只能用const。函數返回值類型和結構體字段不要使用const。

        從一個運行時着色器到下一個運行時着色器之間進行數據類型通信是不存在的。這阻止了同一個着色器在多個頂點和片元之間同時執行。

        沒有存儲修飾符或僅僅使用const修飾符的全局變量,可能在main()執行前進行初始化。Uniforms, attributes和varyings可能沒有初始化器。

4.3.1默認存儲修飾符

        如果在全局變量前沒有修飾符,那麼它們就與應用程序和其他處理器上的着色器沒有關聯。對於全局或本地的無修飾符變量,聲明都會在其所屬的那個處理器上分配內存。這個變量將提供對分配的內存的讀寫訪問。

4.3.2常量修飾符

         命名的編譯時常量可以用const聲明。任何使用const聲明的變量在其所屬的着色器中均是隻讀的。將變量聲明爲常量可以減少使用硬連線的數字常數。const可以用來修飾任何基本數據類型。通常const變量在聲明的同時要進行初始化:

               const vec3 zAxis = vec3 (0.0, 0.0, 1.0);

         結構體字段不能使用const修飾嗎,但是變量可以,並通過構造器進行初始化。包含數組的數組和結構體不能聲明爲常量,因爲它們不能被初始化。

4.3.3  Attribute

         attribute修飾符用於聲明通過OpenGL ES應用程序傳遞到頂點着色器中的變量值。在其它任何非頂點着色器的着色器中聲明attribute變量是錯誤的。在頂點着色器被程序使用之前,attribute變量是隻讀的。attribute變量的值通過OpenGL ES頂點API或者作爲頂點數組的一部分被傳進頂點着色器。它們傳遞頂點屬性值到頂點着色器,並且在每一個運行的頂點着色器中都會改變。attribute修飾符只能修飾float, vec2, vec3, vec4,mat2,mat3,mat4。attribute變量不能聲明爲數組或結構體。如:

               attribute vec4 position;

               attribute vec3 normal;

               attribute vec2 texCoord;

        大家可能希望圖形硬件有極少量的固定位置來傳遞頂點屬性。所以,OpenGL ES爲每一個非矩陣變量賦予了升級到4個浮點數值的空間,如vec4。在OpenGL ES中,可以使用的屬性變量個數是有限制的,如果超過這個限制,將會引起鏈接錯誤。(聲明瞭但沒有使用的屬性變量不會受到這個限制。)一個浮點數屬性也要受到這個限制,所以你應該儘量將四個毫不相關的float變量打包成一個pack,以優化底層硬件的兼容性。一個mat4和使用4個vec4變量是一致的,同理,一個mat3和使用3個vec3變量是一致的,一個mat2和使用2個vec2變量是一致的。着色語言和API隱藏了到底這些空間是如何被矩陣使用的。屬性變量需要被聲明爲全局變量。


4.3.4  Uniform

        uniform修飾符用來修飾那些在整個圖元被處理的過程中保持不變的全局變量。所有的uniform變量都是隻讀的,可以通過應用程序調用API命令初始化,或者通過OpenGL ES間接初始化。

                 uniform vec4 lightPosition;

       uniform修飾符可以和任意基本數據類型一起使用,或者包含基本數據類型元素的數組和結構體。每種類型的着色器的uniform變量的存儲數量是有限制的,如果超過這個限制,將會引起編譯時或鏈接時錯誤。聲明瞭但是沒有被靜態使用的uniform變量不會受到這個限制。靜態使用(static use)是指着色器包含變量在預處理以後的一個引用。用戶定義的uniform變量和着色器中被靜態使用的內建uniform變量將共同決定有沒有超出可用uniform存儲範圍。

       當頂點着色器和片元着色器被鏈接到一起,它們將共享同一個名稱空間。這就意味着,所有被連接到同一個可執行程序的着色器中的同名變量必須也同時具有相同的類型和精度。


4.3.4  Varying

        varying變量提供了頂點着色器,片元着色器和二者通訊控制模塊之間的接口。頂點着色器計算每個頂點的值(如顏色,紋理座標等)並將它們寫到varying變量中。頂點着色器也會從varying變量中讀值,獲取和它寫入相同的值。如果從頂點着色器中讀取一個尚未被寫入的varying變量,將返回未定義值。

        通過定義,每個頂點的varying變量以一種透視校正的方式被插入到正在渲染的圖元上。如果是單採樣,插值爲片元中心。如果是多采樣,插值可以是像素中的任何地方,包括片元中心或者其中一個片元採樣。

        片元着色器會讀取varying變量的值,並且被讀取的值將會作爲插值器,作爲圖元中片元位置的一個功能信息。varying變量對於片元着色器來說是隻讀的。

        在頂點和片元着色器中都有聲明的同名varying變量的類型必須匹配,否則將引起鏈接錯誤。

        下表總結了頂點和片元着色器匹配的規則:


  術語“靜態使用”意思是在預處理之後,着色器至少包含一個訪問varying變量的語句,即使這個語句沒有真正執行過。

               varying vec3 normal;

         varying修飾符只能用在float, vec2, vec3, vec4, mat2, mat3, mat4和包含這些類型元素的數組上,不能用於修飾結構體。

         varying變量需要聲明爲全局變量。


4.4參數修飾符

        函數參數修飾符有如下幾種:

(1)<none: default>,默認情況下,是in

(2)in,作爲函數的傳入參數

(3)out,作爲函數的傳出參數

(4)inout,即作爲傳入參數,又作爲傳出參數


4.5精度和精度修飾符

4.5.1範圍和精度

        用於存儲和展示浮點數、整數變量的範圍和精度依賴於數值的源(varying,uniform,紋理查找,等等),是不是頂點或者片元着色器,還有其他一些底層實現的細節。最低存儲需要通過精度修飾符來聲明。典型地,精度操作必須要保留變量包含的精度存儲。僅有的例外是需要大量複雜計算的內建函數,如atan(),返回值的精度低於聲明的精度。

       強烈建議頂點語言提供一種匹配IEEE單精度浮點數或更高精度的浮點數的浮點範圍和精度。這就需要頂點語言提供浮點變量的範圍至少是(-2^62,  2^62),精度至少是65536。    

      頂點語言必須提供一種至少16位,加上一個符號位的整數精度。

      片元語言提供與頂點着色器相同的浮點數範圍和精度是很有必要的,但不是必須的。這就需要片元語言提供的浮點數的範圍至少是(-16384,+16384),精度至少是1024。

      片元語言必須提供一種至少10爲,加上一個符號位的整數精度。

      

4.5.2精度修飾符

      任何浮點數或者整數聲明前面都可以添加如下精度修飾符:


舉例:

lowp float color;

varying mediump vec2 Coord;

lowp ivec2 foo(lowp mat3);

highp mat4 m;

       

         精度修飾符聲明瞭底層實現存儲這些變量必須要使用的最小範圍和精度。實現可能會使用比要求更大的範圍和精度,但絕對不會比要求少。

         一下是精度修飾符要求的最低範圍和精度:


   Floating Point Magnitude Range是非零值量級的範圍。對於Floating Point Precision,relative意思是任何度量的值的精度都是相對於這個值的。對於所有的精度級別,0必須被精確的表示出來。任何不能提供着色器存儲變量所聲明的精度的實現都會引起一個編譯或鏈接錯誤。

        對於高精度和中級精度,整型範圍必須可以準確地轉化成相應的相同精度修飾符所表示的float型。這樣的話,highp int 可以被轉換成highp float, mediump int 可以被轉換成mediump float,但是lowp int 不能轉換成相應的lowp float。

        頂點語言要求編譯和鏈接任何lowp, mediump和highp應用都不能出現錯誤。

        片元語言要求編譯和鏈接任何lowp, mediump應用都不能出現錯誤。但是highp支持是可選的。

        字符常量和布爾型沒有精度修飾符.當浮點數和整數構造器不含帶有精度修飾符的參數時也不需要精度修飾符。

        在這段文檔中,操作包含運算符,內建函數和構造器,操作數包含函數參數和構造器參數。

        對於精度沒有定義的常量表達式或子表達式,評估的精度結果是所有操作數中的最高精度(mediump或者highp) 。帶評估的常量表達式必須是固定不變的,並且在編譯期進行。

        另外,對於沒有精度修飾符的操作數,精度將來自於其他操作數。如果所有的操作數都沒有精度,那麼接着看使用計算結果的其他表達式。這個操作是遞歸的,直到找到一個有精度的操作符爲止。如果必要,這個操作也包含賦值運算的左值,初始化聲明的變量,函數形參,函數返回值.如果這樣依然不能決定精度,如果組成表達式的所有操作數都沒有精度,如果結果沒有被賦值,也沒有當作參數傳進函數,那麼將使用默認或更大的類型.當這種情況出現在片元着色器中,默認的精度必須被定義.

        比如:

uniform highp float h1;

highp float h2 = 2.3*4.7;操作和結果都是高精度

mediump float m;

m = 3.7*h1*h2;//所有操作都是高精度

h2 = m * h1;//操作是高精度

m = h2 - h1;//操作是高精度

h2 = m + m;//加法和結果都是mediump精度

void f(highp p);

f(3.3);//3.3將作爲高精度值傳入函數

4.5.3默認精度修飾符

         precision precision-qualifier type;

         precision可以用來確定默認精度修飾符。type可以是int或float或採樣器類型,precision-qualifier可以是lowp, mediump, 或者highp。任何其他類型和修飾符都會引起錯誤。如果type是float類型,那麼該精度(precision-qualifier)將適用於所有無精度修飾符的浮點數聲明(標量,向量,矩陣)。如果type是int類型,那麼該精度(precision-qualifier)將適用於所有無精度修飾符的整型數聲明(標量,向量)。包括全局變量聲明,函數返回值聲明,函數參數聲明,和本地變量聲明等。沒有聲明精度修飾符的變量將使用和它最近的precision語句中的精度。


         在頂點語言中有如下預定義的全局默認精度語句:

precision highp float;

precision highp int;

precision lowp sampler2D;

precision lowp samplerCube;


         在片元語言中有如下預定義的全局默認精度語句:

precision mediump int;

precision lowp sampler2D;

precision lowp samplerCube;

         片元語言沒有默認的浮點數精度修飾符。因此,對於浮點數,浮點數向量和矩陣變量聲明,要麼聲明必須包含一個精度修飾符,要不默認的精度修飾符在之前已經被聲明過了。


4.5.4可用的精度修飾符

        內建宏GL_FRAGMENT_PRECISION_HIGH在支持highp精度的片元語言中是定義過的,但在不支持的系統中是未定義的。一旦定義以後,在頂點和片元語言中都可以使用。

                 #defien GL_FRAGMENT_PRECISION_HIGH 1;


4.6變異和invariant修飾符

        在這部分中,變異是指在不同的着色器中的相同語句返回不同的值的可能性.舉個例子,兩個頂點着色器都使用相同的表達式來設置gl_Position,並且當着色器執行時傳進表達式的值也是一樣的.完全有可能,由於兩個着色器獨立的編譯環境,當着色器運行時賦給gl_Position的值不一定會相同.在這個例子中,會引起多路算法的幾何對齊問題.

        通常,着色器之間的這種變異是允許的.如果想避免這種變異的發生,變量可以使用invariant來聲明.

4.6.1invariant修飾符

        爲確保一個特定的輸出變量是不變的,可以使用invariant修飾符.它可以修飾之前已經定義過的變量,如:

                  invariant gl_Position;

也可以用在變量的聲明當中:

                  invariant varying mediump vec3 Color;

        僅如下變量可以聲明爲invariant:

(1)頂點着色器中內建的特定輸出變量

(2)頂點着色器中輸出varying變量

(3)片元着色器中特定的輸入變量

(4)片元着色器中的輸入varying變量

(5)片元着色器中內建的輸出變量

          invariant後面還可以跟一個用逗號隔開的之前聲明的標識符列表.

          爲了確保兩個着色器中特定的輸出變量不發生變異.還應遵循以下規則:

(1)頂點和片元着色器中的輸出變量都聲明爲invariant

(2)相同的值必須輸入到賦給輸出變量的表達式或控制流的所有着色器輸入變量.

(3)輸出變量上的任何紋理函數調用在使用紋理格式,紋理像素值和紋理過濾時都需要設置成相同的方式.

(4)所有的輸入變量都以相同的方式操作.


       初始時,默認的所有輸出變量被允許變異.如果想強制所有輸出變量都不可變,那麼在着色器所有的變量聲明之前使用

                   #pragma STDGL invariant(all)

4.6.2着色器中的不變體

        當一個值被存到一個變量中,我們通常假設它是一個常量,除非顯示的去更改它的值.然而,在優化處理期間,編譯期可能會重新計算一個值而不是將它存到寄存器中.因爲操作的精度沒有被完全指定(如,低精度的操作會被轉成中等精度或高精度),重新計算的值有可能就和原來的值不一致.

        在着色器中變體是允許的.如果要避免變體,可以使用invariant修飾符或invariant pragma.

precision mediump;

vec4 col;

vec2 a = ...;

........

col = texture2D(tex, a);//此時a的值假設爲a1

..............

col = texture2D(tex, a);//此時a的值假設爲a2,但是有可能a1不等於a2

如果強制成常量,可以使用:

                   #pragma STDGL invariant(all)


例子二:

vec2 m = ...;

vec2 n = ...;

vec2 a = m + n;

vec2 b = m + n;//沒法保證a和b完全相等


4.6.3常量表達式的不變體

        常量表達式必須要保證是不變體.一個特定的表達式在相同的還是不同的着色器中都必須有相同的結果.這包括同一個表達式出現在同一個頂點和片元着色器中,或出現在不同的頂點和片元着色器中.

        如果滿足以下條件,常量表達式必須得出相同的值:

(1)表達式的輸入值相同

(2)執行的操作相同並且順序也相同

(3)所有操作均以相同的精度執行

4.6.4不變體和鏈接裝置

        在頂點和片元着色器中聲明的不變體varying變量必須要匹配.對於內建的特定變量,當且僅當gl_Position被聲明爲invariant時,gl_FragCoord纔可以被聲明爲invariant.同樣的,當且僅當gl_PositionSize被聲明爲invariant時,gl_PointCoord纔可以被聲明爲invariant.將gl_FrontFacing聲明爲invariant是錯誤的.gl_FrontFacing的不變體和gl_Position的不變體是一樣的.

4.7修飾順序

        當需要使用多個修飾時,它們必須遵循嚴格的順序:

(1)invariant-qualifier   storage-qualifier   precision-qualifier

(2)storage-qualifier    parameter-qualifier    precision-qualifier
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章