跳轉到內容

GLSL 程式設計/Blender/光澤紋理

來自華夏公益教科書
從國際空間站 (ISS) 看,太平洋上空的日落,帶有鏡面高光。

本教程涵蓋部分光澤、紋理表面的每個畫素的照明

它結合了紋理球體教程平滑鏡面高光教程的著色器程式碼,以計算每個畫素的照明,該照明具有用於漫反射的材質顏色,該顏色由紋理的 RGB 分量決定,以及由同一紋理的 A 分量決定的鏡面反射強度。如果您還沒有閱讀紋理球體教程平滑鏡面高光教程,這是一個很好的機會。

光澤對映

[編輯 | 編輯原始碼]

照明紋理表面教程介紹了透過紋理影像的 RGB 分量確定漫反射的材質常數的概念。這裡我們擴充套件了這種技術,並透過同一紋理影像的 A(alpha)分量確定鏡面反射的強度。僅使用一個紋理提供了顯著的效能優勢,特別是由於在某些情況下,RGBA 紋理查詢與 RGB 紋理查詢一樣昂貴。

如果紋理影像的“光澤”(即鏡面反射的強度)編碼在 RGBA 紋理影像的 A(alpha)分量中,我們可以簡單地將鏡面反射的材質常數乘以紋理影像的 alpha 分量。是在鏡面高光教程中介紹的,出現在 Phong 反射模型的鏡面反射項中

如果乘以紋理影像的 alpha 分量,該項將達到其最大值(即表面光澤)alpha 為 1 的地方,而 alpha 為 0 的地方為 0(即表面根本沒有光澤)。

地球地圖,透明水,即 alpha 分量對水為 0,對陸地為 1。

每個畫素的照明著色器程式碼

[編輯 | 編輯原始碼]

著色器程式碼是平滑鏡面高光教程中每個畫素的照明和紋理球體教程中的紋理的組合。類似於照明紋理表面教程textureColor 中紋理顏色的 RGB 分量乘以環境光和漫射光。

在左側的特定紋理影像中,alpha 分量對水為 0,對陸地為 1。但是,應該是水是光澤的,而陸地不是。因此,對於這個特定的影像,我們應該將鏡面材質顏色乘以(1.0 - textureColor.a)。另一方面,通常的光澤貼圖需要乘以textureColor.a。(請注意對著色器程式進行此類更改是多麼容易。)

頂點著色器如下

         varying vec4 position; 
            // position of the vertex (and fragment) in view space 
         varying vec3 varyingNormalDirection; 
            // surface normal vector in view space
         varying vec4 texCoords; // the texture coordinates 

         void main()
         {                              
            position = gl_ModelViewMatrix * gl_Vertex; 
            varyingNormalDirection = 
               normalize(gl_NormalMatrix * gl_Normal);             

            texCoords = gl_MultiTexCoord0;
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }

片段著色器變為

         varying vec4 position; 
            // position of the vertex (and fragment) in view space 
         varying vec3 varyingNormalDirection; 
            // surface normal vector in view space
         varying vec4 texCoords; // interpolated texture coordinates 
         uniform sampler2D textureUnit;

         void main()
         {
            vec3 normalDirection = normalize(varyingNormalDirection);
            vec3 viewDirection = -normalize(vec3(position)); 
            vec3 lightDirection;
            float attenuation; 

            vec2 longitudeLatitude = vec2(
               (atan(texCoords.y, texCoords.x)/3.1415926+1.0)*0.5, 
               1.0 - acos(texCoords.z) / 3.1415926);
               // unusual processing of texture coordinates

            vec4 textureColor = 
               texture2D(textureUnit, longitudeLatitude);
            
            if (0.0 == gl_LightSource[0].position.w) 
               // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = 
                  normalize(vec3(gl_LightSource[0].position));
            } 
            else // point light or spotlight (or other kind of light) 
            {
               vec3 positionToLightSource = 
                  vec3(gl_LightSource[0].position - position);
               float distance = length(positionToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(positionToLightSource);
 
               if (gl_LightSource[0].spotCutoff <= 90.0) // spotlight?
               {
                  float clampedCosine = max(0.0, dot(-lightDirection, 
                     gl_LightSource[0].spotDirection));
                  if (clampedCosine < gl_LightSource[0].spotCosCutoff) 
                     // outside of spotlight cone?
                  {
                     attenuation = 0.0;
                  }
                  else
                  {
                     attenuation = attenuation * pow(clampedCosine, 
                        gl_LightSource[0].spotExponent);   
                  }
               }
            }

            vec3 ambientLighting = vec3(gl_LightModel.ambient) 
               * vec3(textureColor);
              
            vec3 diffuseReflection = attenuation 
               * vec3(gl_LightSource[0].diffuse) * vec3(textureColor)
               * max(0.0, dot(normalDirection, lightDirection));
 
            vec3 specularReflection;
            if (dot(normalDirection, lightDirection) < 0.0) 
               // light source on the wrong side?
            {
               specularReflection = vec3(0.0, 0.0, 0.0); 
                  // no specular reflection
            }
            else // light source on the right side
            {
               specularReflection = attenuation 
                  * vec3(gl_LightSource[0].specular) 
                  * vec3(gl_FrontMaterial.specular) 
                  * (1.0 - textureColor.a) 
                     // for usual gloss maps: "* textureColor.a"
                  * pow(max(0.0, dot(reflect(-lightDirection, 
                  normalDirection), viewDirection)), 
                  gl_FrontMaterial.shininess);
            }

            gl_FragColor = vec4(ambientLighting + diffuseReflection 
               + specularReflection, 1.0);
         }

紋理和球體必須按紋理球體教程中所述進行設定。本教程還解釋瞭如何在 Python 指令碼中設定統一變數textureUnit

對上述特定紋理影像,此著色器的有用修改是將漫射材質顏色設定為 alpha 分量為 0 的深藍色。

每個頂點的照明著色器程式碼

[編輯 | 編輯原始碼]

平滑鏡面高光教程中所述,鏡面高光通常不會使用每個頂點的照明很好地渲染。但是,有時由於效能限制別無選擇。為了在照明紋理表面教程的著色器程式碼中包含光澤對映,應該用以下程式碼替換片段著色器

            varying diffuseColor;
            varying specularColor;
            varying vec4 texCoords;
            uniform sampler2D textureUnit;
 
            void main()
            {
               vec2 longitudeLatitude = vec2(
                   (atan(texCoords.y, texCoords.x)/3.1415926+1.0)*0.5, 
                   1.0 - acos(texCoords.z) / 3.1415926);
               vec4 textureColor = 
                  texture2D(textureUnit, longitudeLatitude);
               gl_FragColor = vec4(diffuseColor * vec3(textureColor)
                  + specularColor * (1.0 - textureColor.a), 1.0);
            }

請注意,通常的光澤貼圖需要乘以textureColor.a而不是(1.0 - textureColor.a)

恭喜!您完成了關於光澤對映的重要教程。我們已經瞭解了

  • 什麼是光澤對映。
  • 如何為每個畫素的照明實現它。
  • 如何為每個頂點的照明實現它。

進一步閱讀

[編輯 | 編輯原始碼]

如果您還想了解更多


< GLSL 程式設計/Blender

除非另有說明,否則本頁上的所有示例原始碼均授予公有領域。
華夏公益教科書