【GLSL教程】(三)在OpenGL中向shader傳遞信息 【轉】


http://blog.csdn.net/racehorse/article/details/6634830

引言

一個OpenGL程序可以用多種方式和shader通信。注意這種通信是單向的,因為shader的輸出只能是渲染到某些目標,比如顏色和深度緩存。

OpenGL的部分狀態可以被shader訪問,因此程序改變OpenGL某些狀態就可以與shader進行通信了。例如一個程序想把光的顏色傳給shader,可以直接調用OpenGL接口,就像使用固定功能流水線時做的那樣。

不 過,使用OpenGL狀態並不是設置shader中使用數據的直觀方式。比如一個shader需要一個表示時間變化的變量來計算動畫,在OpenGL狀態 中就沒有現成的變量可用。當然,你可以使用沒有用到的“鏡面光截止角度(cutoffangle)”這樣一個變量表示時間,但顯然讓人難以接受。

幸運的是,GLSL允許用戶自定義變量,實現OpenGL應用程序與shader通信。有了這個功能,你就可以命名一個叫做timeElapsed的變量表示經過的時間。

上文的討論涉及到了GLSL提供的兩種類型修飾符(更多的類型將在后面提到):

·一致變量(Uniform)

·屬性(Attribute)

在shader中定義的變量如果用這兩種類型修飾符,表示對shader來說,它們是只讀的。下面將詳細講述怎樣使用這些類型的變量。

還有一種將變量送給shader的方法:使用紋理。一個紋理不止可以表示一張圖片,它還可以表示一個數組。事實上,你完全可以決定如何在shader中解釋紋理數據,即使它真是一幅圖片。

 

數據類型和變量

下面是GLSL中的基本數據類型:

·float

·bool

·int

浮點類型與C中類似,布爾類型可以為true或false。這些基本類型可以組成2、3或4維向量,如下所示:

·vec{2,3,4} a vector of 2,3,or 4 floats

·bvec{2,3,4} bool vector

·ivec{2,3,4} vector of integers

GLSL還包括2×2、3×3或4×4型矩陣,因為這些矩陣類型在圖形處理中很常用:

·mat2

·mat3

·mat4

此外,還有一組用來實現紋理訪問的特殊類型,它們被稱為采樣器(sampler),在讀取紋理值(也稱為紋素texel)時用到。下面就是紋理采樣用到的數據類型:

·sampler1D – for 1D textures

·sampler2D – for 2D textures

·sampler3D – for 3D textures

·samplerCube – for cube map textures

·sampler1DShadow – for shadow maps

·sampler2DShadow – for shadow maps

在GLSL中,可以像C一樣聲明和訪問數組,但是不能在聲明時初始化數組。GLSL還可以定義結構體:

 

  1. struct dirlight  
  2. {  
  3.     vec3 direction;  
  4.     vec3 color;  
  5. };  

變量聲明一個基本類型變量的方法與C類似,你還可以在聲明它的同時進行初始化。

 

  1. float a,b;       // two vector (yes, the comments are like in C)  
  2. int c = 2;       // c is initialized with 2  
  3. bool d = true;  // d is true  

聲明其它類型變量也是按照這種方法,但是初始化與C語言有區別。GLSL非常依賴構造函數實現初始化和類型轉換。

 

  1. float b = 2;          // incorrect, there is no automatic type casting  
  2. float e = (float)2; // incorrect, requires constructors for type casting  
  3. int a = 2;  
  4. float c = float(a); // correct. c is 2.0  
  5. vec3 f;                // declaring f as a vec3  
  6. vec3 g = vec3(1.0,2.0,3.0); // declaring and initializing g  

在GLSL中使用一些變量初始化其它變量是非常靈活的。你只需要給出需要的數據成員即可。請看下面的例子:

  1. vec2 a = vec2(1.0,2.0);  
  2. vec2 b = vec2(3.0,4.0);  
  3. vec4 c = vec4(a,b)   // c = vec4(1.0,2.0,3.0,4.0);  
  4. vec2 g = vec2(1.0,2.0);  
  5. float h = 3.0;  
  6. vec3 j = vec3(g,h);  

矩陣的初始化也是類似方法,矩陣包含很多種構造函數,下面的例子給出了一些初始化矩陣的構造函數:

 

  1. mat4 m = mat4(1.0)   // initializing the diagonal of the matrix with 1.0  
  2. vec2 a = vec2(1.0,2.0);  
  3. vec2 b = vec2(3.0,4.0);  
  4. mat2 n = mat2(a,b); // matrices are assigned in column major order  
  5. mat2 k = mat2(1.0,0.0,1.0,0.0); // all elements are specified  

下面的例子給出了初始化結構體的方法:

 

  1. struct dirlight     // type definition  
  2. {  
  3.     vec3 direction;  
  4.     vec3 color;  
  5. };  
  6. dirlight d1;  
  7. dirlight d2 = dirlight(vec3(1.0,1.0,0.0),vec3(0.8,0.8,0.4));  

在GLSL中還有一些實用的選擇子(selector),可以簡化我們的操作並讓代碼更簡潔。訪問一個向量可以使用如下的方法:

  1. vec4 a = vec4(1.0,2.0,3.0,4.0);  
  2. float posX = a.x;  
  3. float posY = a[1];  
  4. vec2 posXY = a.xy;  
  5. float depth = a.w  

在上面的代碼片段中,可以使用x、y、z、w來訪問向量成員。如果是顏色的話可以使用r、g、b、a,如果是紋理坐標的話可以使用s、t、p、q。注意表示紋理坐標通常是使用s、t、r、q,但r已經表示顏色中的紅色(red)了,所以紋理坐標中需要使用p來代替。矩陣的選擇子可以使用一個或兩個參數,比如m[0]或者m[2][3]。第一種情況選擇了第一列,第二種情況選擇了一個數據成員。

對於結構體來說,可以像在C語言中一樣訪問其成員。所以訪問前面定義的結構體,可以使用如下的代碼:

 

  1. d1.direction = vec3(1.0,1.0,1.0);  

變量修飾符

修飾符給出了變量的特殊含義,GLSL中有如下修飾符:

·const – 聲明一個編譯期常量。

·attribute– 隨不同頂點變化的全局變量,由OpenGL應用程序傳給頂點shader。這個修飾符只能用在頂點shader中,在shader中它是一個只讀變量。

·uniform– 隨不同圖元變化的全局變量(即不能在glBegin/glEnd中設置),由OpenGL應用程序傳給shader。這個修飾符能用在頂點和片斷shader中,在shader中它是一個只讀變量。

·varying –用於頂點shader和片斷shader間傳遞的插值數據,在頂點shader中可寫,在片斷shader中只讀。

 

一致變量(Uniform Variables)

不同於頂點屬性在每個頂點有其自己的值,一個一致變量在一個圖元的繪制過程中是不會改變的,所以其值不能在glBegin/glEnd中設置。一致變量適合描述在一個圖元中、一幀中甚至一個場景中都不變的值。一致變量在頂點shader和片斷shader中都是只讀的。

首先你需要獲得變量在內存中的位置,這個信息只有在連接程序之后才可獲得。注意,對某些驅動程序,在獲得存儲位置前還必須使用程序(調用glUseProgram)。

獲取一個一致變量的存儲位置只需要給出其在shader中定義的變量名即可:

 

  1. GLint glGetUniformLocation(GLuint program, const char *name);  
  2. 參數:  
  3. ·program – the hanuler to the program  
  4. ·name – the name of the variable  

返回值就是變量位置,可以用此信息設置變量的值。根據變量的數據類型不同,有一系列函數可以用來設置一致變量。用來設置浮點值的一組函數如下:

  1. void glUniform1f(GLint location, GLfloat v0);  
  2. void glUniform2f(GLint location, GLfloat v0, GLfloat v1);  
  3. void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);  
  4. void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);  
  5. 或者  
  6. GLint glUniform{1,2,3,4}fv(GLint location, GLsizei count, GLfloat *v);  
  7. 參數:  
  8. ·location – the previously queried location  
  9. ·v0,v1,v2,v3 – float values  
  10. ·count – the number of elements in the array  
  11. ·v – an array of floats  

對integer類型也有一組類似的函數,不過要用i替換函數中的f。對bool類型沒有專門的函數,但可以使用整數的0和1來表示真假。一旦你使用了一致變量數組,那么就必須使用向量版本的函數。

對sampler變量,使用函數glUniform1i和glUniform1iv。

矩陣也是一種GLSL的數據類型,所以也有一組針對矩陣的函數:

 

  1. GLint glUniformMatrix{2,3,4}fv(GLint location, GLsizei count, GLboolean transpose, GLfloat *v);  
  2. 參數:  
  3. location – the previously queried location.  
  4. count – the number of matrices. 1 if a single matrix is being set, or n for an array of n matrices.  
  5. transpose – wheter to transpose the matrix values. A value of 1 indicates that the matrix values are specified in row major order, zero is column major order  
  6. v – an array of floats.  

還有一點要注意的是:使用這些函數之后,變量的值將保持到程序再次連接之時。一旦進行重新連接,所有變量的值將被重置為0。

最后是一些示例代碼。假設一個shader中使用了如下變量:

 

  1. uniform float specIntensity;  
  2. uniform vec4 specColor;  
  3. uniform float t[2];  
  4. uniform vec4 colors[3];  

在OpenGL程序中可以使用下面的代碼設置這些變量:

 

  1. GLint loc1,loc2,loc3,loc4;  
  2. float specIntensity = 0.98;  
  3. float sc[4] = {0.8,0.8,0.8,1.0};  
  4. float threshold[2] = {0.5,0.25};  
  5. float colors[12] = {0.4,0.4,0.8,1.0,  
  6.                 0.2,0.2,0.4,1.0,  
  7.                 0.1,0.1,0.1,1.0};  
  8.   
  9. loc1 = glGetUniformLocation(p,"specIntensity");  
  10. glUniform1f(loc1,specIntensity);  
  11.   
  12. loc2 = glGetUniformLocation(p,"specColor");  
  13. glUniform4fv(loc2,1,sc);  
  14.   
  15. loc3 = glGetUniformLocation(p,"t");  
  16. glUniform1fv(loc3,2,threshold);  
  17.   
  18. loc4 = glGetUniformLocation(p,"colors");  
  19. glUniform4fv(loc4,3,colors);  

例子代碼的下載地址:http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl2_2.0.zip

注 意設置一個數組(例子中的t)與設置四元向量(例子中的colors和specColor)的區別。中間的count參數指在shader中聲明的數組元 素數量,而不是在OpenGL程序中聲明的。所以雖然specColor包含4個值,但glUniform4fv函數中的參數是1,因為它只是一個向量。 另一種設置specColor的方法:

 

  1. loc2 = glGetUniformLocation(p,"specColor");  
  2. glUniform4f(loc2,sc[0],sc[1],sc[2],sc[3]);  

GLSL中還可以獲取數組中某個變量的地址。比如,可以獲得t[1]的地址。下面的代碼片段展示了設置t數組元素的另一種方法:

 

  1. loct0 = glGetUniformLocation(p,"t[0]");  
  2. glUniform1f(loct0,threshold[0]);  
  3.   
  4. loct1 = glGetUniformLocation(p,"t[1]");  
  5. glUniform1f(loct1,threshold[1]);  

注意在glGetUniformLocation中使用方括號指示的變量。

 

屬性變量(Attribute Variables)

在前一節提到,一致變量只能針對一個圖元全體,就是說不能在glBegin和glEnd之間改變。

如果要針對每個頂點設置變量,那就需要屬性變量了。事實上屬性變量可以在任何時刻更新。在頂點shader中屬性變量是只讀的。因為它包含的是頂點數據,所以在片斷shader中不能直接應用。

與一致變量相似,首先你需要獲得變量在內存中的位置,這個信息只有在連接程序之后才可獲得。注意,對某些驅動程序,在獲得存儲位置前還必須使用程序。

 

  1. GLint glGetAttribLocation(GLuint program,char *name);  
  2. 參數:  
  3. program – the handle to the program.  
  4. name – the name of the variable  

上述函數調用的返回變量在存儲器中的地址。下面就可以為它指定一個值,類似一致變量,每種數據類型都有對應的函數。

 

  1. void glVertexAttrib1f(GLint location, GLfloat v0);  
  2. void glVertexAttrib2f(GLint location, GLfloat v0, GLfloat v1);  
  3. void glVertexAttrib3f(GLint location, GLfloat v0, GLfloat v1,GLfloat v2);  
  4. void glVertexAttrib4f(GLint location, GLfloat v0, GLfloat v1,,GLfloat v2, GLfloat v3);  
  5. 或者  
  6. GLint glVertexAttrib{1,2,3,4}fv(GLint location, GLfloat *v);  
  7. 參數:  
  8. location – the previously queried location.  
  9. v0,v1,v2,v3 – float values.  
  10. v – an array of floats.  

對於integer類型,也有一組類似的函數。與一致變量不同,這里向量版的函數並不支持對向量數組的賦值,所以函數參數用向量或是分別指定的效果沒有太大區別,就好像OpenGL中glColor3f和glColor3fv的關系。下面是一個簡單的例子,假定頂點shader中聲明了一個名為height的浮點屬性變量,在程序連接之后可以進行如下操作:

 

  1. loc = glGetAttribLocation(p,"height");  

在執行渲染的代碼中間可以為shader中的變量賦值:

 

  1. glBegin(GL_TRIANGLE_STRIP);  
  2.     glVertexAttrib1f(loc,2.0);  
  3.     glVertex2f(-1,1);  
  4.     glVertexAttrib1f(loc,2.0);  
  5.     glVertex2f(1,1);  
  6.     glVertexAttrib1f(loc,-2.0);  
  7.     glVertex2f(-1,-1);  
  8.     glVertexAttrib1f(loc,-2.0);  
  9.     glVertex2f(1,-1);  
  10. glEnd();  

例子代碼的下載地址:

http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl3_2.0.zip

頂點數組和屬性變量也可以一起使用。首先需要使能數組,使用如下函數:

 

  1. void glEnableVertexAttribArray(GLint loc);  
  2. 參數:  
  3. loc – the location of the variable.  

接下來使用函數提交包含數據的數組指針:

 

  1. void glVertexAttribPointer(GLint loc, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);  
  2. 參數:  
  3. loc – the location of the variable.  
  4. size – the number of components per element, for instance: 1 for float; 2 for vec2; 3 for vec3, and so on.  
  5. type – The data type associated: GL_FLOAT is an example.  
  6. normalized – if set to 1 then the array values will be normalized, converted to a range from -1 to 1 for signed data, or 0 to 1 for unsigned data.  
  7. stride – the spacing between elements. Exactly the same as in OpenGL.  
  8. pointer – pointer to the array containing the data.  

下面是示例代碼,首先執行初始化,定義了頂點數組和屬性數組。

 

  1. float vertices[8] = {-1,1, 1,1, -1,-1, 1,-1};  
  2. float heights[4] = {2,2,-2,-2};  
  3. ...  
  4. loc = glGetAttribLocation(p,"height");  
  5.   
  6. glEnableClientState(GL_VERTEX_ARRAY);  
  7. glEnableVertexAttribArray(loc);  
  8. glVertexPointer(2,GL_FLOAT,0,vertices);  
  9. glVertexAttribPointer(loc,1,GL_FLOAT,0,0,heights);  

接下來的渲染步驟與OpenGL中的通常做法一致,比如調用glDrawArrays。示例源代碼下載地址:http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl4_2.0.zip

 

易變變量(Varying Variables)

前 面說過,shader包括兩種類型:頂點shader和片斷shader。為了計算片斷的值,往往需要訪問頂點的插值數據。例如,當使用逐片斷光照時,我 們需要知道當前片斷的法線,但是在OpenGL中只為每個頂點指定了法線。頂點shader可以訪問這些法線,而片斷shader不能,因為法線是 OpenGL程序作為屬性變量指定的。

頂 點變換后的數據移動到流水線的下一個階段,在這個階段通過使用連接信息,生成了所有圖元並完成片斷化。對每個片斷,有一組變量會被自動進行插值並提供給片 斷shader,這些都是固定功能。片斷的顏色就是這么處理的,到達片斷shader的顏色就是組成圖元的頂點顏色插值的結果。

像片斷shader接收到的這種插值產生的變量,就是“易變變量”類型。GLSL包含一些預先定義的易變變量,例如前面提到的顏色。用戶也可以自己定義易變變量,它們必須同時在頂點shader和片斷shader中聲明:

 

  1. varying float intensity;  

一個易變變量必須先在頂點shader中聲明,然后計算每個頂點的變量值。在片斷shader中,接收這個變量通過插值得到的結果,注意此時這個變量是只讀的。

 

語句和函數

控制流語句

與C語言類似,GLSL中有類似if-else的條件語句,for,while,do-while等循環語句。

 

  1. if (bool expression)  
  2.     ...  
  3. else  
  4.     ...  
  5.   
  6. for (initialization; bool expression; loop expression)  
  7.     ...  
  8.   
  9. while (bool expression)  
  10.     ...  
  11.   
  12. do  
  13.     ...  
  14. while (bool expression)  

GLSL也有跳轉語句:

·continue – available in loops, causes a jump to thenext iteration of the loop

·break – available in loops, causes an exit of theloop

·discard

最后的discard關鍵字只能在片斷shader中使用,它將在不寫入幀緩存或者深度緩存的情況下,終止當前片斷的shader程序。

 函數

與C語言類似,shader也是由函數組成的結構化程序。至少每類shader都必須包含一個如下方式聲明的主函數:

 

  1. void main()  

此外用戶還可以自定義函數。這些函數像C函數一樣,一般都會有返回值,返回值的類型沒有限制,但不能是數組。

函數參數可以有如下修飾符:

·in – for input parameters

·out – for outputs of the function. The returnstatement is also an option for sending the result of a function.

·inout – for parameters that are both input andoutput of a function

如果沒有指定修飾符,默認情況下為in類型。

最后還有兩點要注意:

·允許函數重載,只要參數不同。

·在標准中沒有定義遞歸行為。

結束本節之前來看一個函數的例子:

 

    1. vec4 toonify(in float intensity)  
    2. {  
    3.     vec4 color;  
    4.     if (intensity > 0.98)  
    5.        color = vec4(0.8,0.8,0.8,1.0);  
    6.     else if (intensity > 0.5)  
    7.        color = vec4(0.4,0.4,0.8,1.0);  
    8.     else if (intensity > 0.25)  
    9.        color = vec4(0.2,0.2,0.4,1.0);  
    10.     else  
    11.        color = vec4(0.1,0.1,0.1,1.0);   
    12.   
    13.     return(color);  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM