【GLSL教程】(四)shder的簡單示例

GLSL的Hello World

這一節中包含一個最基本的shader,它提供如下功能:頂點變換然後使用單一的顏色渲染圖元。

頂點shader

前面已經說過,頂點shader負責完成頂點變換。這裏將按照固定功能的方程完成頂點變換。

固定功能流水線中一個頂點通過模型視圖矩陣以及投影矩陣進行變換,使用如下公式:

  1. vTrans = projection * modelview *incomingVertex 
vTrans = projection * modelview *incomingVertex
首先GLSL需要訪問OpenGL狀態,獲得公式中的前兩個矩陣。前面講過,GLSL可以獲取某些OpenGL狀態信息的,這兩個矩陣當然包括在內。可以通過預先定義的一致變量來獲取它們:

  1. uniform mat4 gl_ModelViewMatrix; 
  2. uniform mat4 gl_ProjectionMatrix; 
uniform mat4 gl_ModelViewMatrix;
uniform mat4 gl_ProjectionMatrix;
接下來需要得到輸入的頂點。通過預先定義的屬性變量,所有的頂點將可以一個個傳入頂點shader中。

  1. attribute vec4 gl_Vertex; 
attribute vec4 gl_Vertex;
爲了輸出變換後的頂點,shader必須寫入預先定義的vec4型變量gl_Position中,注意這個變量沒有修飾符。

現在我們可以寫一個僅僅進行頂點變換的頂點shader了。注意所有其他功能都將喪失,比如沒有光照計算。頂點shader必須有一個main函數,如下面的代碼所示:

  1. void main() 
  2.     gl_Position =gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex; 
void main()
{
    gl_Position =gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;
}
上面代碼中變換每個頂點時,投影矩陣都將乘上模型視圖矩陣,這顯然非常浪費時間,因爲這些矩陣不是隨每個頂點變化的。注意這些矩陣是一致變量

GLSL提供一些派生的矩陣,也就是說gl_ModelViewProjectionMatrix是上面兩個矩陣的乘積,所以頂點shader也可以寫成下面這樣:

  1. void main() 
  2.     gl_Position =gl_ModelViewProjectionMatrix * gl_Vertex; 
void main()
{
    gl_Position =gl_ModelViewProjectionMatrix * gl_Vertex;
}
上面的操作能夠獲得和固定功能流水線相同的結果嗎?理論上是如此,但實際上對頂點變換操作的順序可能會不同。頂點變換通常在顯卡中是高度優化的任務,所以有一個利用了這種優化的特定函數用來處理這個任務。這個神奇的函數如下:

  1. vec4 ftransform(void); 
vec4 ftransform(void);
使用這個函數的另一個原因是float數據類型的精度限制。由於數據精度的限制,當使用不同的順序計算時,可能得到不同的結果,因此GLSL提供這個函數保證獲得最佳性能的同時,還能得到與固定功能流水線相同的結果。

這個函數按照與固定功能相同的步驟對輸入頂點進行變換,然後返回變換後的頂點。所以shader可以重新寫成如下形式:

  1. void main() 
  2.     gl_Position =ftransform(); 
void main()
{
    gl_Position =ftransform();
}
片斷shader

片斷shader也有預先定義的變量gl_FragColor,可以向其中寫入片斷的顏色值。下面的代碼就是一個片斷shader,將所有片斷繪製成淡藍色:

  1. void main() 
  2.     gl_FragColor =vec4(0.4,0.4,0.8,1.0); 
void main()
{
    gl_FragColor =vec4(0.4,0.4,0.8,1.0);
}
可以在此獲得本節例子的源碼:

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


顏色shader

GLSL可以讀取一些OpenGL狀態,在本節我們將學習如何訪問在OpenGL中設置的glColor變量。

GLSL有一個屬性變量記錄當前顏色,也提供易變變量從頂點shader向片斷shader傳遞顏色值。

  1. attribute vec4 gl_Color; 
  2. varying vec4 gl_FrontColor; // writable onthe vertex shader 
  3. varying vec4 gl_BackColor; // writable onthe vertex shader 
  4. varying vec4 gl_Color; // readable on thefragment shader 
attribute vec4 gl_Color;
varying vec4 gl_FrontColor; // writable onthe vertex shader
varying vec4 gl_BackColor; // writable onthe vertex shader
varying vec4 gl_Color; // readable on thefragment shader
變量使用思想如下:

1、OpenGL程序通過glColor傳送顏色信息。

2、頂點shader通過屬性gl_Color接收顏色值。

3、頂點shader計算正面和反面的顏色,然後分別保存在gl_FrontColor和gl_BackColor中。

4、片斷shader接收易變變量gl_Color中存儲的插值產生的顏色,由當前圖元的方向決定顏色是gl_FrontColor還是gl_BackColor插值產生的。

5、片斷shader根據易變變量gl_Color設置gl_FragColor。

前 面說過頂點shader和片斷shader中傳遞的易變變量要有相同的名字,但這裏是個例外,頂點shader中的gl_FrontColor和 gl_BackColor會根據圖元的方向,自動轉變爲片斷shader中的gl_Color。還要注意屬性變量gl_Color和易變變量 gl_Color沒有衝突,因爲前者只存在於頂點shader,後者只存在於片斷shader。

下面是頂點shader的例子,只計算了正面顏色:

  1. void main() 
  2.     gl_FrontColor =gl_Color; 
  3.     gl_Position =ftransform(); 
void main()
{
    gl_FrontColor =gl_Color;
    gl_Position =ftransform();
}

片斷shader更加簡單:

  1. void main() 
  2.     gl_FragColor = gl_Color; 
void main()
{
    gl_FragColor = gl_Color;
}
基於GLEW的源代碼:

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

扁平shader(Flatten Shader)

着色器編程讓我們可以探索一些新效果,本節的例子展示了用奇怪的方法操作頂點得到的效果。

首先我們要得到一個扁平的3D模型,只需要在應用模型視圖變換時將模型頂點的z座標設爲0就行了。下面是頂點shader的代碼:

  1. void main(void
  2.     vec4 v = vec4(gl_Vertex); 
  3.     v.z = 0.0; 
  4.  
  5.     gl_Position =gl_ModelViewProjectionMatrix * v; 
void main(void)
{
    vec4 v = vec4(gl_Vertex);
    v.z = 0.0;

    gl_Position =gl_ModelViewProjectionMatrix * v;
}
我們先將gl_Vertex變量複製到一個局部變量v中。gl_Vertex是一個GLSL提供的屬性變量,所以在頂點shader中它是隻讀的。

片斷shader與“GLSL中的Hello World”一節相同,就只用設置一種顏色。

一個扁平的茶壺效果如下:


更進一步,我們需要在z座標上使用一個正弦函數。將z座標作爲x座標的函數,這樣茶杯將呈現波浪的效果:

  1. void main(void
  2.     vec4 v =vec4(gl_Vertex); 
  3.     v.z = sin(5.0*v.x)*0.25; 
  4.     gl_Position =gl_ModelViewProjectionMatrix * v; 
void main(void)
{
    vec4 v =vec4(gl_Vertex);
    v.z = sin(5.0*v.x)*0.25;
    gl_Position =gl_ModelViewProjectionMatrix * v;
}
最後我們需要加入一些頂點動畫效果。爲了達到這個目的我們需要增加一個變量記錄變化的時間,或者幀數。一個頂點shader是無法記錄不同頂點值的,更不用 說記錄不同的幀了。所以我們需要在OpenGL程序中定義這個變量,然後作爲一致變量傳遞給shader。假設在OpenGL程序中有一個名爲time的 幀計數器,在shader中有個同名的一致變量。

頂點shader的代碼如下:

  1. uniform float time; 
  2.  
  3. void main(void
  4.     vec4 v =vec4(gl_Vertex); 
  5.     v.z = sin(5.0*v.x +time*0.01)*0.25; 
  6.     gl_Position =gl_ModelViewProjectionMatrix * v; 
uniform float time;

void main(void)
{
    vec4 v =vec4(gl_Vertex);
    v.z = sin(5.0*v.x +time*0.01)*0.25;
    gl_Position =gl_ModelViewProjectionMatrix * v;
}
在有關一致變量的小節講過,在OpenGL程序中需要兩個步驟:

·setup: 獲取一致變量的存儲位置

·render: 更新一致變量

設置(setup)步驟只有一條語句:

  1. loc =glGetUniformLocation(p,"time"); 
loc =glGetUniformLocation(p,"time");
這裏p是程序的句柄,time與頂點shader中定義的一致變量名稱相同。變量loc是Glint類型的,必須定義在下面的渲染(render)函數也可以訪問到的地方。渲染函數如下所示:

  1. void renderScene(void
  2.     glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); 
  3.     glLoadIdentity(); 
  4.     gluLookAt(0.0,0.0,5.0, 
  5.           0.0,0.0,0.0, 
  6.           0.0f,1.0f,0.0f); 
  7.     glUniform1f(loc, time); 
  8.  
  9.     glutSolidTeapot(1); 
  10.     time+=0.01; 
  11.     glutSwapBuffers(); 
void renderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    gluLookAt(0.0,0.0,5.0,
          0.0,0.0,0.0,
          0.0f,1.0f,0.0f);
    glUniform1f(loc, time);

    glutSolidTeapot(1);
    time+=0.01;
    glutSwapBuffers();
}
函數中的變量time在程序一開始初始化,然後每幀都會進行自增運算。

本節的GLEW源代碼:

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

發佈了9 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章