跳轉到內容

GLSL 程式設計/Blender/照明紋理表面

來自華夏公益教科書,開放的世界,開放的書籍
阿波羅 8 號拍攝的地球升起。

本教程涵蓋紋理表面的逐頂點照明

它結合了紋理球體教程鏡面高光教程的著色器程式碼,以計算使用紋理確定的漫反射材質顏色進行照明。如果你還沒有閱讀紋理球體教程鏡面高光教程,這將是一個很好的機會來閱讀它們。

紋理和漫反射逐頂點照明

[編輯 | 編輯原始碼]

紋理球體教程中,紋理顏色被用作片段著色器的輸出。然而,也可以將紋理顏色用作照明計算中的任何引數,特別是材質常數用於漫反射,這在漫反射教程中介紹過。它出現在Phong 反射模型的漫反射部分

其中該方程使用不同的材質常數來表示三種顏色分量,即紅色、綠色和藍色。透過使用紋理來確定這些材質常數,它們可以在表面上變化。

著色器程式碼

[編輯 | 編輯原始碼]

鏡面高光教程中的逐頂點照明相比,這裡的頂點著色器計算了兩個不同的顏色:diffuseColor 在片段著色器中乘以紋理顏色,specularColor 只是鏡面項,不應該乘以紋理顏色。這是完全合理的,但由於歷史原因(即能力較弱的舊圖形硬體),這有時被稱為“分離的鏡面顏色”。

         varying vec3 diffuseColor; 
            // the diffuse Phong lighting computed in the vertex shader
         varying vec3 specularColor; 
            // the specular Phong lighting computed in the vertex shader
         varying vec4 texCoords; // the texture coordinates 
 
         void main()
         {                              
            vec3 normalDirection = 
               normalize(gl_NormalMatrix * gl_Normal);
            vec3 viewDirection = 
               -normalize(vec3(gl_ModelViewMatrix * gl_Vertex)); 
            vec3 lightDirection;
            float attenuation;
 
            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 vertexToLightSource = 
                  vec3(gl_LightSource[0].position 
                  - gl_ModelViewMatrix * gl_Vertex);
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
 
               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); 
               // without material color!
 
            vec3 diffuseReflection = attenuation 
               * vec3(gl_LightSource[0].diffuse) 
               * max(0.0, dot(normalDirection, lightDirection)); 
               // without material color!
 
            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) 
                  * pow(max(0.0, dot(reflect(-lightDirection, 
                  normalDirection), viewDirection)), 
                  gl_FrontMaterial.shininess);
            }
 
            diffuseColor = ambientLighting + diffuseReflection;
            specularColor = specularReflection;
            texCoords = gl_MultiTexCoord0;
            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
         }

片段著色器使用紋理顏色對diffuseColor進行調製,並新增specularColor

         varying vec3 diffuseColor; 
            // the interpolated diffuse Phong lighting
         varying vec3 specularColor; 
            // the interpolated specular Phong lighting
         varying vec4 texCoords; 
            // the interpolated texture coordinates 
         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);
               // unusual processing of texture coordinates

            gl_FragColor = vec4(diffuseColor 
               * vec3(texture2D(textureUnit, longitudeLatitude))
               + specularColor, 1.0);
         }

為了將紋理影像分配給此著色器,你應該按照紋理球體教程中討論的步驟操作;特別是,Python 指令碼必須設定統一的textureUnit的值,例如使用

shader.setSampler('textureUnit', 0)

恭喜,你已經到達了結尾。我們已經瞭解了

  • 紋理和逐頂點照明通常是如何結合的。
  • 什麼是“分離的鏡面顏色”。

進一步閱讀

[編輯 | 編輯原始碼]

如果你還想了解更多

  • 關於Phong 反射模型的漫反射項,你應該閱讀漫反射教程
  • 關於逐頂點照明或Phong 反射模型的其餘部分,即環境光和鏡面項,你應該閱讀鏡面高光教程
  • 關於紋理的基礎知識,你應該閱讀紋理球體教程


< GLSL 程式設計/Blender

除非另有說明,否則本頁上的所有示例原始碼都歸屬於公共領域。
華夏公益教科書