GLSL基礎

GLSL語言基礎

from http://www.kankanews.com/ICkengine/archives/120870.shtml

變量

GLSL的變量命名方式與C語言類似。變量的名稱可以使用字母,數字以及下劃線,但變量名不能以數字開頭,還有變量名不能以gl_作爲前綴,這個是GLSL保留的前綴,用於GLSL的內部變量。當然還有一些GLSL保留的名稱是不能夠作爲變量的名稱的。

 

基本類型

除了布爾型,整型,浮點型基本類型外,GLSL還引入了一些在着色器中經常用到的類型作爲基本類型。這些基本類型都可以作爲結構體內部的類型。如下表:

 

類型 描述
void 跟C語言的void類似,表示空類型。作爲函數的返回類型,表示這個函數不返回值。
bool 布爾類型,可以是true 和false,以及可以產生布爾型的表達式。
int 整型 代表至少包含16位的有符號的整數。可以是十進制的,十六進制的,八進制的。
float 浮點型
bvec2 包含2個布爾成分的向量
bvec3 包含3個布爾成分的向量
bvec4 包含4個布爾成分的向量
ivec2 包含2個整型成分的向量
ivec3 包含3個整型成分的向量
ivec4 包含4個整型成分的向量
mat2 或者 mat2x2 2×2的浮點數矩陣類型
mat3或者mat3x3 3×3的浮點數矩陣類型
mat4x4 4×4的浮點矩陣
mat2x3 2列3行的浮點矩陣(OpenGL的矩陣是列主順序的)
mat2x4 2列4行的浮點矩陣
mat3x2 3列2行的浮點矩陣
mat3x4 3列4行的浮點矩陣
mat4x2 4列2行的浮點矩陣
mat4x3 4列3行的浮點矩陣
sampler1D 用於內建的紋理函數中引用指定的1D紋理的句柄。只可以作爲一致變量或者函數參數使用
sampler2D 二維紋理句柄
sampler3D 三維紋理句柄
samplerCube cube map紋理句柄
sampler1DShadow 一維深度紋理句柄
sampler2DShadow 二維深度紋理句柄

結構體

 

結構體

結構體可以組合基本類型和數組來形成用戶自定義的類型。在定義一個結構體的同時,你可以定義一個結構體實例。或者後面再定義。

struct surface {float indexOfRefraction;

vec3 color;float turbulence;

} mySurface;

surface secondeSurface;

你可以通過=爲結構體賦值,或者使用 ==,!=來判斷兩個結構體是否相等。

mySurface = secondSurface;

mySurface == secondSurface;

只有結構體中的每個成分都相等,那麼這兩個結構體纔是相等的。訪問結構體的內部成員使用. 來訪問。

vec3 color = mySurface.color + secondSurface.color;

結構體至少包含一個成員。固定大小的數組也可以被包含在結構體中。GLSL的結構體不支持嵌套定義。只有預先聲明的結構體可以嵌套其中。

struct myStruct {

  vec3 points[3]; //固定大小的數組是合法的

  surface surf;  //可以,之前已經定義了

  struct velocity {  //不合法float speed;

    vec3 direction;

  } velo;

  subSurface sub; //不合法,沒有預先聲明;};struct subSurface {  int id;
};

 

數組

GLSL中只可以使用一維的數組。數組的類型可以是一切基本類型或者結構體。下面的幾種數組聲明是合法的:

surface mySurfaces[];
vec4 lightPositions[8];
vec4 lightPos[] = lightPositions;const int numSurfaces = 5;
surface myFiveSurfaces[numSurfaces];float[5] values;

指定顯示大小的數組可以作爲函數的參數或者使返回值,也可以作爲結構體的成員.數組類型內建了一個length()函數,可以返回數組的長度。

lightPositions.length() //返回數組的大小 8

最後,你不能定義數組的數組。

 

修飾符

變量的聲明可以使用如下的修飾符。

修飾符 描述
const 常量值必須在聲明是初始化。它是隻讀的不可修改的。
attribute 表示只讀的頂點數據,只用在頂點着色器中。數據來自當前的頂點狀態或者頂點數組。它必須是全局範圍聲明的,不能再函數內部。一個attribute可以是浮點數類型的標量,向量,或者矩陣。不可以是數組或則結構體
uniform 一致變量。在着色器執行期間一致變量的值是不變的。與const常量不同的是,這個值在編譯時期是未知的是由着色器外部初始化的。一致變量在頂點着色器和片段着色器之間是共享的。它也只能在全局範圍進行聲明。
varying 頂點着色器的輸出。例如顏色或者紋理座標,(插值後的數據)作爲片段着色器的只讀輸入數據。必須是全局範圍聲明的全局變量。可以是浮點數類型的標量,向量,矩陣。不能是數組或者結構體。
centorid varying 在沒有多重採樣的情況下,與varying是一樣的意思。在多重採樣時,centorid varying在光柵化的圖形內部進行求值而不是在片段中心的固定位置求值。
invariant (不變量)用於表示頂點着色器的輸出和任何匹配片段着色器的輸入,在不同的着色器中計算產生的值必須是一致的。所有的數據流和控制流,寫入一個invariant變量的是一致的。編譯器爲了保證結果是完全一致的,需要放棄那些可能會導致不一致值的潛在的優化。除非必要,不要使用這個修飾符。在多通道渲染中避免z-fighting可能會使用到。
in 用在函數的參數中,表示這個參數是輸入的,在函數中改變這個值,並不會影響對調用的函數產生副作用。(相當於C語言的傳值),這個是函數參數默認的修飾符
out 用在函數的參數中,表示該參數是輸出參數,值是會改變的。
inout 用在函數的參數,表示這個參數即是輸入參數也是輸出參數。

 

內置變量

內置變量可以與固定函數功能進行交互。在使用前不需要聲明。頂點着色器可用的內置變量如下表:

名稱 類型 描述
gl_Color vec4 輸入屬性-表示頂點的主顏色
gl_SecondaryColor vec4 輸入屬性-表示頂點的輔助顏色
gl_Normal vec3 輸入屬性-表示頂點的法線值
gl_Vertex vec4 輸入屬性-表示物體空間的頂點位置
gl_MultiTexCoordn vec4 輸入屬性-表示頂點的第n個紋理的座標
gl_FogCoord float 輸入屬性-表示頂點的霧座標
gl_Position vec4 輸出屬性-變換後的頂點的位置,用於後面的固定的裁剪等操作。所有的頂點着色器都必須寫這個值。
gl_ClipVertex vec4 輸出座標,用於用戶裁剪平面的裁剪
gl_PointSize float 點的大小
gl_FrontColor vec4 正面的主顏色的varying輸出
gl_BackColor vec4 背面主顏色的varying輸出
gl_FrontSecondaryColor vec4 正面的輔助顏色的varying輸出
gl_BackSecondaryColor vec4 背面的輔助顏色的varying輸出
gl_TexCoord[] vec4 紋理座標的數組varying輸出
gl_FogFragCoord float 霧座標的varying輸出

片段着色器的內置變量如下表:

名稱 類型 描述
gl_Color vec4 包含主顏色的插值只讀輸入
gl_SecondaryColor vec4 包含輔助顏色的插值只讀輸入
gl_TexCoord[] vec4 包含紋理座標數組的插值只讀輸入
gl_FogFragCoord float 包含霧座標的插值只讀輸入
gl_FragCoord vec4 只讀輸入,窗口的x,y,z和1/w
gl_FrontFacing bool 只讀輸入,如果是窗口正面圖元的一部分,則這個值爲true
gl_PointCoord vec2 點精靈的二維空間座標範圍在(0.0, 0.0)到(1.0, 1.0)之間,僅用於點圖元和點精靈開啓的情況下。
gl_FragData[] vec4 使用glDrawBuffers輸出的數據數組。不能與gl_FragColor結合使用。
gl_FragColor vec4 輸出的顏色用於隨後的像素操作
gl_FragDepth float 輸出的深度用於隨後的像素操作,如果這個值沒有被寫,則使用固定功能管線的深度值代替

 

表達式

 

操作符

GLSL語言的操作符與C語言相似。如下表(操作符的優先級從高到低排列)

操作符 描述
() 用於表達式組合,函數調用,構造
[] 數組下標,向量或矩陣的選擇器
. 結構體和向量的成員選擇
++ – 前綴或後綴的自增自減操作符
+ – ! 一元操作符,表示正 負 邏輯非
* / 乘 除操作符
+ - 二元操作符 表示加 減操作
<> <= >= == != 小於,大於,小於等於, 大於等於,等於,不等於 判斷符
&& || ^^ 邏輯與 ,或,  異或
?: 條件判斷符
= += –= *=  /= 賦值操作符
, 表示序列

像 求地址的& 和 解引用的 * 操作符不再GLSL中出現,因爲GLSL不能直接操作地址。類型轉換操作也是不允許的。 位操作符(&,|,^,~, <<, >> ,&=, |=, ^=, <<=, >>=)是GLSL保留的操作符,將來可能會被使用。還有求模操作(%,%=)也是保留的。

 

數組訪問

數組的下標從0開始。合理的範圍是[0, size - 1]。跟C語言一樣。如果數組訪問越界了,那行爲是未定義的。如果着色器的編譯器在編譯時知道數組訪問越界了,就會提示編譯失敗。

vec4 myColor, ambient, diffuse[6], specular[6];

myColor = ambient + diffuse[4] + specular[4];

 

構造函數

構造函數可以用於初始化包含多個成員的變量,包括數組和結構體。構造函數也可以用在表達式中。調用方式如下:

vec3 myNormal = vec3(1.0, 1.0, 1.0);

greenTint = myColor + vec3(0.0, 1.0, 0.0);

ivec4 myColor = ivec4(255);

還可以使用混合標量和向量的方式來構造,只要你的元素足以填滿該向量。

vec4 color = vec4(1.0, vec2(0.0, 1.0), 1.0);

vec3 v = vec3(1.0, 10.0, 1.0);

vec3 v1 = vec3(v);

vec2 fv = vec2(5.0, 6.0);

float f = float(fv); //用x值2.5構造,y值被捨棄

對於矩陣,OpenGL中矩陣是列主順序的。如果只傳了一個值,則會構造成對角矩陣,其餘的元素爲0.

mat3 m3 = mat3(1.0);

構造出來的矩陣式:

1.0 0.0 0.0

0.0 1.0 0.0

0.0 0.0 1.0

mat2 matrix1 = mat2(1.0, 0.0, 0.0, 1.0);

mat2 matrix2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));

mat2 matrix3 = mat2(1.0); 

mat2 matrix4 = mat2(mat4(2.0)); //會取 4×4矩陣左上角的2×2矩陣。

構造函數可以用於標量數據類型的轉換。GLSL不支持隱式或顯示的轉換,只能通過構造函數來轉。其中int轉爲float值是一樣的。float轉爲int則小數部分被丟棄。int或float轉爲bool,0和0.0轉爲false,其餘的值轉爲true. bool轉爲int或float,false值轉爲0和0.0,true轉爲1和1.0.

float f = 1.7;

int I = int(f); // I = 1

數組的初始化,可以在構造函數中傳入值來初始化數組中對應的每一個值。

ivec2 position[3] = ivec2[3]((0,0), (1,1), (2,2));

ivec2 pos2[3] = ivec2[]((3,3), (2,1), (3,1));

構造函數也可以對結構體進行初始化。其中順序和類型要一一對應。

struct surface {  int  index;
  vec3 color;  float rotate;
};

surface mySurface = surface(3, vec3(red, green, blue), 0.5);

 

成分選擇

向量中單獨的成分可以通過{x,y,z,w},{r,g,b,a}或者{s,t,p,q}的記法來表示。這些不同的記法用於頂點,顏色,紋理座標。在成分選擇中,你不可以混合使用這些記法。其中{s,t,p,q}中的p替換了紋理的r座標,因爲與顏色r重複了。下面是用法舉例:

vec3 myVec = {0.5, 0.35, 0.7};float r = myVec.r;float myYz = myVec.yz;float myQ = myVec.q;//出錯,數組越界訪問,q代表第四個元素float myRY = myVec.ry; //不合法,混合使用記法

較特殊的使用方式,你可以重複向量中的元素,或者顛倒其順序。如:

vec3 yxz = myVec.yxz; //調換順序vec4 mySSTT = myVec.sstt; //重複其中的值

在賦值是,也可以選擇你想要的順序,但是不能重複其中的成分。

vec4 myColor = {0.0, 1.0, 2.0, 1.0};
myColor.x = -1.0;
myColor.yz = vec2(3.0, 5.0);
myColor.wx = vec2(1.0, 3.0);
myColor.zz = vec2(2.0, 3.0); //不合法

我們也可以通過使用下標來訪問向量或矩陣中的元素。如果越界那行爲將是未定義的。

float myY = myVec[1];

在矩陣中,可以通過一維的下標來獲得該列的向量(OpenGL的矩陣是列主順序的)。二維的小標來獲得向量中的元素。

mat3 myMat = mat3(1.0);
vec3 myVec = myMat[0]; //獲得第一列向量 1.0, 0.0, 0.0float f = myMat[0][0]; // 第一列的第一個向量。

 

控制流

 

循環

與C和C++相似,GLSL語言也提供了for, while, do/while的循環方式。使用continue跳入下一次循環,break結束循環。

for (l = 0; l < numLights; l++)
{if (!lightExists[l])continue;
    color += light[l];
}while (i < num)
{
    sum += color[i];
    i++;
}do{
    color += light[lightNum];
    lightNum--;
}while (lightNum > 0)

 

if/else

color = unlitColor;if (numLights > 0)
{
    color = litColor;
}else{
    color = unlitColor;
}

 

discard

片段着色器中有一種特殊的控制流成爲discard。使用discard會退出片段着色器,不執行後面的片段着色操作。片段也不會寫入幀緩衝區。

if (color.a < 0.9)

discard;

 

函數

在每個shader中必須有一個main函數。main函數中的void參數是可選的,但返回值是void時必須的。

void main(void)
{
 ...
}

GLSL中的函數,必須是在全局範圍定義和聲明的。不能在函數定義中聲明或定義函數。函數必須有返回類型,參數是可選的。參數的修飾符(in, out, inout, const等)是可選的。

//函數聲明bool isAnyNegative(const vec4 v);//函數調用void main(void)
{bool isNegative = isAnyNegative(gl_Color);
    ...
}//定義bool isAnyNegative(const vec4 v)
{if (v.x < 0.0 || v.y < 0.0 || v.z < 0.0 || v.w < 0.0)return true;elsereturn false;
}

結構體和數組也可以作爲函數的參數。如果是數組作爲函數的參數,則必須制定其大小。在調用傳參時,只傳數組名就可以了。

vec4 sumVectors(int sumSize, vec4 v[10]);void main()
{
    vec4 myColors[10];
    ...
    vec4 sumColor = sumVectors(5, myColors);
}

vec4 sumVectors(int sumSize, vec4 v[10])
{int i = 0;
    vec4 sum = vec4(0.0);for(; i < sumSize; ++i)
    {
        sum += v[i]; 
    }return sum;
}

GLSL的函數是支持重載的。函數可以同名但其參數類型或者參數個數不同即可。

float sum(float a, float b)
{return a + b;
}

vec3 sum(vec3 v1, vec3 v2)
{return v1 + v2;
}

GLSL中函數遞歸是不被允許的。其行爲是未定義的。

GLSL中提供了許多內建的函數,來方便我們的使用。可以在官方手冊中查找相關的函數http://www.opengl.org/sdk/docs/man/

GLSL指南 http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf

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