歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> OpenGL超級寶典學習筆記——GLSL語言基礎

OpenGL超級寶典學習筆記——GLSL語言基礎

日期:2017/3/1 9:32:10   编辑:Linux編程

變量

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 2x2的浮點數矩陣類型 mat3或者mat3x3 3x3的浮點數矩陣類型 mat4x4 4x4的浮點矩陣 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)); //會取 4x4矩陣左上角的2x2矩陣。

構造函數可以用於標量數據類型的轉換。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

OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見 http://www.linuxidc.com/Linux/2013-10/91413.htm

OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm

OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm

Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm

OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm

Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm

如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm

更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581

Copyright © Linux教程網 All Rights Reserved