GLSL語法跟C語言非常相似:
1.數據類型:
GLSL包含下面幾種簡單的數據類型
float
bool :false or ture
int
向量:
vec {2,3,4} 長度為2, 3, 4的float向量
bvec {2,3,4} 長度為2, 3, 4的bool向量
ivec {2,3,4} 長度為2, 3, 4的int向量
矩陣:
mat2 2*2的浮點矩陣
mat3 3*3的浮點矩陣
mat4 4*4的浮點矩陣
以上三種矩陣可以簡寫為mat2 mat3 mat4
矩陣的行和列並沒有規定相等,因為可以使用mat2*3 mat 4*2等方法來聲明行數和列數
一種特殊的數據類型:取樣器--用於紋理采樣
sampler1D 訪問一個一維紋理
sampler2D 訪問一個二維紋理
sampler3D 訪問一個三維紋理
samplerCube 訪問一個立方體紋理
sampler1DShadow 訪問一個帶對比的一維深度紋理
sampler2DShadow 訪問一個帶對比的二維深度紋理
GLSL提供了類似C語言的用戶定義結構:
struct dirlight{
vec3 direction;
vec3 color;
};
變量限定符:
限定符賦給變量特殊的含義:
const-- 用於聲明非可寫的編譯時常量變量
attribute-- 用於經常更改的信息,只可以再頂點着色器中使用
uniform-- 用於不經常更改的信息,用於頂點着色器和片元着色器
varying-- 用於從頂點着色器傳遞到片元着色器的插值信息
控制流:
GLSL的控制流 與C++非常類似,可以使用for while以及do-while實現循環,也可以使用if和if-else進行選擇,不過if語句中的變量聲明,只是在最近的硬件中才提供
函數:
GLSL也提供了一些特殊的實現:
continue
break
discard --只可用於片元着色器,當控制流遇到這個關鍵字時,正在處理的片元就會被標記為將要丟棄
函數
main() 可以返回除了數組外的任何類型
對於函數的參數 可以使用下面幾種限定符
in -- 復制進函數但不在返回時復制,在函數內部仍然是可寫的
out--只在返回時復制,是可讀的
inout 復制進函數並在返回時復制
如果沒有指定限定符,默認情況下為in
函數可以通過參數類型重載,但是不能僅僅通過返回類型重載,同樣,因為不會執行參數類型自動提升,所以調用函數時參數類型必須完全匹配
函數不能被遞歸調用
GLSL Vertex shader內置的輸入變量,注意這些變量都是不可更改的
attribute vec4 gl_Color; 頂點數據字段的Diffuse顏色
attribute vec4 gl_SecondaryColor; 頂點數據字段的Specular顏色
attribute vec4 gl_Normal; 頂點法線
attribute vec4 gl_Vertex; 頂點位置
attribute vec4 gl_MultiTexCoord0; 8組貼圖坐標
attribute vec4 gl_MultiTexCoord1;
attribute vec4 gl_MultiTexCoord2;
attribute vec4 gl_MultiTexCoord3;
attribute vec4 gl_MultiTexCoord4;
attribute vec4 gl_MultiTexCoord5;
attribute vec4 gl_MultiTexCoord6;
attribute vec4 gl_MultiTexCoord7;
attribute vec4 gl_MultiTexCoord0;
attribute vec4 gl_MultiTexCoord1;
attribute vec4 gl_FogCoord; 使用霧效果的參考數值
在編寫shader時,可以把這些輸入數據所代表的功能重新定義,名稱只是用來讓傳入數據時有個規則可循而已,C++調用glVertexPointer所指到的vetex buffer數據,在GLSL中可以通過gl_Vertex變量來獲得。
Vertex Shader的輸出數據時使用的內置變量:
vec4 gl_posotion; 用來設置頂點轉換到屏幕坐標的位置,Vertex Shader一定要去更新這個數值
float gl_pointSize; 是啟動PointSprite功能時,用來設置矩形大小的數值
vec4 gl_ClipVertex; 如果啟用了Clip Plane功能,gl_ClipVertex可以放入用來與Clip Plane平面做測試用的位置
下面的輸出數據在Vertex Shader中用來輸出數據,在Fragment Sahder也可以使用這些變量,但是是用來讀取數據:
araying Vec4 gl_FrontColor; 對正面做不同的光照計算 ,這兩組顏色分主要顏色和次要顏色 代表的是固管的Diffuse值
varying vec4 gl_BackColor; 背面
varying vec4 gl_FrontSecondDaryColor; 固管的Specular值
varying vec4 gl_BackSecondaryColor;
varying vec4 gl_TexCoord[gl_MaxTextureCoords]; glTextCoord[0]是指第0個貼圖坐標
varying vec4 gl_FogFragCoord;
Fragment Sahder除了可以從上面幾個所列出的變量獲得內插結果外,還可以從另外兩個內置變量得到一些無法從Vertex Shader獲得的數值
vec4 gl_FragCoord; gl_FragCoorg.xy代表像素在Framebuffer畫面的位置,gl_FragCoord.z代表這個店在做Z Buffer測試時所用的Z值
bool gl_FrontFacing; 可用來查詢目前正在畫的像素是來自三角形的正面還是來自他的背面
Fragment Shader的內置輸出變量:
vec4 gl_FragColor; 代表畫面所要填入的顏色
vec4 gl_FragData[gl_MaxDrawBuffers]; 用來填入畫面的顏色,用在啟用多個FrameBuffer時,調用gl_FragData填入畫面顏色
vec4 gl_FrData填入畫面顏色
vec4 gl_FragDepth; 用來指定Z Buffer測試時所使用的Z值,這樣就可以不通過頂點內插得到的Z值
對於Vertex Shader來說,除了可通過內置變量來內插數值給Fragment Shader之外,也可以不通過內置變量,
只要在Vertex Shader和Fragment Shader中聲明相同名稱的全局變量,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