跳转到内容

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

除非另有说明,否则本页上的所有示例源代码都归属于公共领域。
华夏公益教科书