【GLSL教程】(八)紋理貼圖

簡單的紋理貼圖(Simple Texture)
爲了在GLSL中應用紋理,我們需要訪問每個頂點的紋理座標。GLSL中提供了一些屬性變量,每個紋理單元一個:
  1. attribute vec4 gl_MultiTexCoord0; 
  2. attribute vec4 gl_MultiTexCoord1; 
  3. attribute vec4 gl_MultiTexCoord2; 
  4. attribute vec4 gl_MultiTexCoord3; 
  5. attribute vec4 gl_MultiTexCoord4; 
  6. attribute vec4 gl_MultiTexCoord5; 
  7. attribute vec4 gl_MultiTexCoord6; 
  8. attribute vec4 gl_MultiTexCoord7; 
attribute vec4 gl_MultiTexCoord0;
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;
GLSL還爲訪問每個紋理的紋理矩陣提供了一個一致變量數組:
  1. uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords]; 
uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];
頂點shader可以通過上面所示的內容訪問OpenGL程序中指定的紋理座標。然後必須爲每個頂點計算紋理座標,並保存在預先定義的易變變量gl_TexCoord[i]中,i表示紋理單元號。
下面這條語句直接複製OpenGL程序中指定的紋理座標,作爲紋理單元0的頂點紋理座標。
  1. gl_TexCoord[0]  = gl_MultiTexCoord0; 
gl_TexCoord[0]  = gl_MultiTexCoord0;
下面是個簡單的例子,在頂點shader中設置紋理單元0的紋理座標。
  1. void main() 
  2.     gl_TexCoord[0] = gl_MultiTexCoord0; 
  3.     gl_Position = ftransform(); 
void main()
{
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
}
如果你想使用紋理矩陣,可以這樣操作:
  1. void main() 
  2.     gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; 
  3.     gl_Position = ftransform(); 
void main()
{
    gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
    gl_Position = ftransform();
}
前面說過,gl_TexCoord是一個易變變量,所以在片斷shder中可以訪問經過插值的紋理座標。
爲了訪問紋理的數值,在片斷shader中有必要聲明一個特殊的變量,對一個2D紋理可以可以這樣寫:
  1. uniform sampler2D tex; 
uniform sampler2D tex;
如果是1D或者3D的紋理,可以改成sampler1D和sampler3D。
這個用戶定義的變量tex包含我們將會使用的紋理單元,通過texture2D函數我們可以得到一個紋素(texel),這是一個紋理圖片中的像素。函數參數分別爲simpler2D以及紋理座標:
  1. vec4 texture2D(sampler2D, vec2); 
vec4 texture2D(sampler2D, vec2);
函數的返回值已經考慮了所有在OpenGL程序中定義的紋理設置,比如過濾、mipmap、clamp等。
我們的片斷shader可以寫成如下形式:
  1. uniform sampler2D tex; 
  2.  
  3. void main() 
  4.     vec4 color = texture2D(tex,gl_TexCoord[0].st); 
  5.     gl_FragColor = color; 
uniform sampler2D tex;

void main()
{
    vec4 color = texture2D(tex,gl_TexCoord[0].st);
    gl_FragColor = color;
}
注意訪問gl_TexCoord時選擇子st的使用。在本教程前面關於數據類型和變量的討論中說過,訪問紋理座標時可以使用如下選擇子:s、t、p、q。(r因爲和rgb選擇子衝突而沒有使用)

本節內容Shader Designer的工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/textureSimple.zip

組合紋理與片斷

OpenGL允許我們通過多種方式將紋理顏色和片斷顏色聯合到一起。下表顯示了RGBA模式時可用的聯合方式:

GL_REPLACE C = Ct A = At
GL_MODULATE C = Ct*Cf A = At*Af
GL_DECAL C = Cf * (1 – At) + Ct * At A = Af
表中Ct和At表示紋理的顏色和alpha值,Cf和Af表示片斷(fragment)的顏色和alpha值,C和A表示最終的顏色和alpha值。
上一節的例子就相當於使用了GL_REPLACE模式。下面我們我們準備在一個立方體上實現與GL_MODULATE等同的效果。兩個shader只計算使用一個白色方向光的散射以及環境光成分,關於材質的完整定義請參照光照有關的章節。
因爲使用了光照,所以頂點shader中必須處理法線信息。必須將法線變換到視圖空間然後歸一化,光線方向向量也必須歸一化(光線方向向量已經由OpenGL變換到了視圖空間)。現在新的頂點shader如下:

  1. varying vec3 lightDir,normal; 
  2.  
  3. void main() 
  4.     normal = normalize(gl_NormalMatrix * gl_Normal); 
  5.  
  6.     lightDir = normalize(vec3(gl_LightSource[0].position)); 
  7.     gl_TexCoord[0] = gl_MultiTexCoord0; 
  8.  
  9.     gl_Position = ftransform(); 
varying vec3 lightDir,normal;

void main()
{
    normal = normalize(gl_NormalMatrix * gl_Normal);

    lightDir = normalize(vec3(gl_LightSource[0].position));
    gl_TexCoord[0] = gl_MultiTexCoord0;

    gl_Position = ftransform();
}
在片斷shader中,光照得到的片斷的顏色和alpha值在cf和af中分別計算。shader中剩餘代碼按照GL_MODULATE的公式計算:
  1. varying vec3 lightDir,normal; 
  2. uniform sampler2D tex; 
  3.  
  4. void main() 
  5.     vec3 ct,cf; 
  6.     vec4 texel; 
  7.     float intensity,at,af; 
  8.  
  9.     intensity = max(dot(lightDir,normalize(normal)),0.0); 
  10.     cf = intensity * (gl_FrontMaterial.diffuse).rgb + 
  11.                   gl_FrontMaterial.ambient.rgb; 
  12.     af = gl_FrontMaterial.diffuse.a; 
  13.  
  14.     texel = texture2D(tex,gl_TexCoord[0].st); 
  15.     ct = texel.rgb; 
  16.     at = texel.a; 
  17.  
  18.     gl_FragColor = vec4(ct * cf, at * af); 
varying vec3 lightDir,normal;
uniform sampler2D tex;

void main()
{
    vec3 ct,cf;
    vec4 texel;
    float intensity,at,af;

    intensity = max(dot(lightDir,normalize(normal)),0.0);
    cf = intensity * (gl_FrontMaterial.diffuse).rgb +
                  gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;

    texel = texture2D(tex,gl_TexCoord[0].st);
    ct = texel.rgb;
    at = texel.a;

    gl_FragColor = vec4(ct * cf, at * af);
}

Shader Designer的工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/textureComb.zip

多重紋理
在GLSL中實現多重紋理十分容易,我們只需要訪問所有紋理即可。因爲我們打算給每個紋理使用相同的紋理座標,所以頂點shader不需要改動。片斷shader中只需要進行些許改動,加上多個紋理的顏色值。
  1. varying vec3 lightDir,normal; 
  2. uniform sampler2D tex; 
  3.  
  4. void main() 
  5.     vec3 ct,cf; 
  6.     vec4 texel; 
  7.     float intensity,at,af; 
  8.  
  9.     intensity = max(dot(lightDir,normalize(normal)),0.0); 
  10.     cf = intensity * (gl_FrontMaterial.diffuse).rgb + 
  11.                   gl_FrontMaterial.ambient.rgb; 
  12.     af = gl_FrontMaterial.diffuse.a; 
  13.  
  14.     texel = texture2D(tex,gl_TexCoord[0].st) + 
  15.           texture2D(l3d,gl_TexCoord[0].st); 
  16.     ct = texel.rgb; 
  17.     at = texel.a; 
  18.  
  19.     gl_FragColor = vec4(ct * cf, at * af); 
varying vec3 lightDir,normal;
uniform sampler2D tex;

void main()
{
    vec3 ct,cf;
    vec4 texel;
    float intensity,at,af;

    intensity = max(dot(lightDir,normalize(normal)),0.0);
    cf = intensity * (gl_FrontMaterial.diffuse).rgb +
                  gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;

    texel = texture2D(tex,gl_TexCoord[0].st) +
          texture2D(l3d,gl_TexCoord[0].st);
    ct = texel.rgb;
    at = texel.a;

    gl_FragColor = vec4(ct * cf, at * af);
}
效果如下:

下面添加點不同的效果:在黑暗中發光。我們希望第二個紋理能在黑暗中發光,在沒有光照時達到最亮,在有光照時變暗。

我們通過兩步計算最終的顏色:首先將第一個紋理與片斷顏色進行modulate計算,然後根據光照強度(indensity)加上第二個紋理單元。
如果indensity是0,第二個紋理單元取最大值,如果indensity爲1,只取第二個紋理單元顏色的10%,當indensity在0和1之間時按這兩個大小進行插值。可以使用smoothstep函數實現這個要求:
  1. genType smoothStep(genType edge0, genType edge1, genType x); 
genType smoothStep(genType edge0, genType edge1, genType x);
如果x <= edge0結果是0,如果x >= edge1結果爲1,如果edge0 < x < edge1結果在0和1之間進行Hermite插值。在本例中我們按如下方式調用:
coef = smoothStep(1.0, 0.2, intensity);
下面的片斷shader實現了需要的效果:
  1. varying vec3 lightDir,normal; 
  2. uniform sampler2D tex,l3d; 
  3.  
  4. void main() 
  5.     vec3 ct,cf,c; 
  6.     vec4 texel; 
  7.     float intensity,at,af,a; 
  8.  
  9.     intensity = max(dot(lightDir,normalize(normal)),0.0); 
  10.  
  11.     cf = intensity * (gl_FrontMaterial.diffuse).rgb + 
  12.                       gl_FrontMaterial.ambient.rgb; 
  13.     af = gl_FrontMaterial.diffuse.a; 
  14.  
  15.     texel = texture2D(tex,gl_TexCoord[0].st); 
  16.  
  17.     ct = texel.rgb; 
  18.     at = texel.a; 
  19.  
  20.     c = cf * ct; 
  21.     a = af * at; 
  22.  
  23.     float coef = smoothstep(1.0,0.2,intensity); 
  24.     c += coef *  vec3(texture2D(l3d,gl_TexCoord[0].st)); 
  25.  
  26.     gl_FragColor = vec4(c, a); 
varying vec3 lightDir,normal;
uniform sampler2D tex,l3d;

void main()
{
    vec3 ct,cf,c;
    vec4 texel;
    float intensity,at,af,a;

    intensity = max(dot(lightDir,normalize(normal)),0.0);

    cf = intensity * (gl_FrontMaterial.diffuse).rgb +
                      gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;

    texel = texture2D(tex,gl_TexCoord[0].st);

    ct = texel.rgb;
    at = texel.a;

    c = cf * ct;
    a = af * at;

    float coef = smoothstep(1.0,0.2,intensity);
    c += coef *  vec3(texture2D(l3d,gl_TexCoord[0].st));

    gl_FragColor = vec4(c, a);
}
Shader Designer的工程下載地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/textureGlow.zip

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