跳转到内容

GLSL 编程/Unity/光泽纹理

来自维基教科书,开放书籍,构建开放世界
从国际空间站 (ISS) 看到的太平洋上,夕阳带着镜面高光。

本教程涵盖了 **部分光泽纹理表面的逐像素光照**。

它结合了 “纹理球体”部分“平滑镜面高光”部分 的着色器代码,计算使用纹理的 RGB 分量确定漫反射材质颜色的逐像素光照,以及使用同一纹理的 A 分量确定镜面反射的强度。如果你没有阅读过这些部分,现在是一个很好的机会来阅读它们。

光泽映射

[编辑 | 编辑源代码]

“光照纹理表面”部分 中,漫反射的材质常量由纹理图像的 RGB 分量决定。在这里,我们扩展了这种技术,并通过同一纹理图像的 A(alpha)分量来确定镜面反射的强度。仅使用一个纹理提供了显着的性能优势,特别是因为在某些情况下,RGBA 纹理查找与 RGB 纹理查找一样昂贵。

如果纹理图像的“光泽”(即镜面反射的强度)编码在 RGBA 纹理图像的 A(alpha)分量中,我们可以简单地将镜面反射的材质常量 与纹理图像的 alpha 分量相乘。 “镜面高光”部分 中介绍,并出现在 Phong 反射模型的镜面反射项中

如果乘以纹理图像的 alpha 分量,则该项达到最大值(即表面是光泽的),其中 alpha 为 1,并且为 0(即表面根本没有光泽),其中 alpha 为 0。

带有透明水域的地球地图,即水域的 alpha 分量为 0,陆地的 alpha 分量为 1。

每个像素光照的着色器代码

[编辑 | 编辑源代码]

着色器代码是 “平滑镜面高光”部分 中的逐像素光照和 “纹理球体”部分 中的纹理的组合。类似于 “光照纹理表面”部分,纹理颜色 textureColor 的 RGB 分量乘以漫反射材质颜色 _Color

在左边的特定纹理图像中,alpha 分量对于水为 0,对于陆地为 1。但是,应该是水是光泽的,而陆地不是。因此,对于这个特定的图像,我们应该将镜面材质颜色乘以 (1.0 - textureColor.a)。另一方面,通常的光泽贴图需要乘以 textureColor.a。(注意对着色器程序进行这种改变是多么容易。)

Shader "GLSL per-pixel lighting with texture" {
   Properties {
      _MainTex ("RGBA Texture For Material Color", 2D) = "white" {} 
      _Color ("Diffuse Material Color", Color) = (1,1,1,1) 
      _SpecColor ("Specular Material Color", Color) = (1,1,1,1) 
      _Shininess ("Shininess", Float) = 10
   }
   SubShader {
      Pass {	
         Tags { "LightMode" = "ForwardBase" } 
            // pass for ambient light and first light source

         GLSLPROGRAM

         // User-specified properties
         uniform sampler2D _MainTex;
         uniform vec4 _Color; 
         uniform vec4 _SpecColor; 
         uniform float _Shininess;

         // The following built-in uniforms (except _LightColor0) 
         // are also defined in "UnityCG.glslinc", 
         // i.e. one could #include "UnityCG.glslinc" 
         uniform vec3 _WorldSpaceCameraPos; 
            // camera position in world space
         uniform mat4 _Object2World; // model matrix
         uniform mat4 _World2Object; // inverse model matrix
         uniform vec4 _WorldSpaceLightPos0; 
            // direction to or position of light source
         uniform vec4 _LightColor0; 
            // color of light source (from "Lighting.cginc")
         
         varying vec4 position; 
            // position of the vertex (and fragment) in world space 
         varying vec3 varyingNormalDirection; 
            // surface normal vector in world space
         varying vec4 textureCoordinates; 

         #ifdef VERTEX
         
         void main()
         {				
            mat4 modelMatrix = _Object2World;
            mat4 modelMatrixInverse = _World2Object; // unity_Scale.w 
               // is unnecessary because we normalize vectors
            
            position = modelMatrix * gl_Vertex;
            varyingNormalDirection = normalize(vec3(
               vec4(gl_Normal, 0.0) * modelMatrixInverse));

            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
            textureCoordinates = gl_MultiTexCoord0;
         }
         
         #endif

         #ifdef FRAGMENT
         
         void main()
         {
            vec3 normalDirection = normalize(varyingNormalDirection);

            vec3 viewDirection = 
               normalize(_WorldSpaceCameraPos - vec3(position));
            vec3 lightDirection;
            float attenuation;

            vec4 textureColor = 
               texture2D(_MainTex, vec2(textureCoordinates));

            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = normalize(vec3(_WorldSpaceLightPos0));
            } 
            else // point or spot light
            {
               vec3 vertexToLightSource = 
                  vec3(_WorldSpaceLightPos0 - position);
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
            }
            
            vec3 ambientLighting = vec3(gl_LightModel.ambient) 
               * vec3(_Color) * vec3(textureColor);

            vec3 diffuseReflection = attenuation * vec3(_LightColor0) 
               * vec3(_Color) * 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(_LightColor0) 
                  * vec3(_SpecColor) * (1.0 - textureColor.a) 
                     // for usual gloss maps: "... * textureColor.a"
                  * pow(max(0.0, dot(
                  reflect(-lightDirection, normalDirection), 
                  viewDirection)), _Shininess);
            }

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

         ENDGLSL
      }

      Pass {	
         Tags { "LightMode" = "ForwardAdd" } 
            // pass for additional light sources
         Blend One One // additive blending 


         GLSLPROGRAM

         // User-specified properties
         uniform sampler2D _MainTex;
         uniform vec4 _Color; 
         uniform vec4 _SpecColor; 
         uniform float _Shininess;

         // The following built-in uniforms (except _LightColor0) 
         // are also defined in "UnityCG.glslinc", 
         // i.e. one could #include "UnityCG.glslinc" 
         uniform vec3 _WorldSpaceCameraPos; 
            // camera position in world space
         uniform mat4 _Object2World; // model matrix
         uniform mat4 _World2Object; // inverse model matrix
         uniform vec4 _WorldSpaceLightPos0; 
            // direction to or position of light source
         uniform vec4 _LightColor0; 
            // color of light source (from "Lighting.cginc")
         
         varying vec4 position; 
            // position of the vertex (and fragment) in world space 
         varying vec3 varyingNormalDirection; 
            // surface normal vector in world space
         varying vec4 textureCoordinates; 

         #ifdef VERTEX
         
         void main()
         {				
            mat4 modelMatrix = _Object2World;
            mat4 modelMatrixInverse = _World2Object; // unity_Scale.w 
               // is unnecessary because we normalize vectors
            
            position = modelMatrix * gl_Vertex;
            varyingNormalDirection = normalize(vec3(
               vec4(gl_Normal, 0.0) * modelMatrixInverse));

            gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
            textureCoordinates = gl_MultiTexCoord0;
         }
         
         #endif

         #ifdef FRAGMENT
         
         void main()
         {
            vec3 normalDirection = normalize(varyingNormalDirection);

            vec3 viewDirection = 
               normalize(_WorldSpaceCameraPos - vec3(position));
            vec3 lightDirection;
            float attenuation;

            vec4 textureColor = 
               texture2D(_MainTex, vec2(textureCoordinates));

            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = normalize(vec3(_WorldSpaceLightPos0));
            } 
            else // point or spot light
            {
               vec3 vertexToLightSource = 
                  vec3(_WorldSpaceLightPos0 - position);
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
            }
            
            vec3 diffuseReflection = attenuation * vec3(_LightColor0) 
               * vec3(_Color) * 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(_LightColor0) 
                  * vec3(_SpecColor) * (1.0 - textureColor.a) 
                     // for usual gloss maps: "... * textureColor.a"
                  * pow(max(0.0, dot(
                  reflect(-lightDirection, normalDirection), 
                  viewDirection)), _Shininess);
            }

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

         ENDGLSL
      }
   } 
   // The definition of a fallback shader should be commented out 
   // during development:
   // Fallback "Specular"
}

对上面特定纹理图像的这个着色器进行一个有用的修改是,将漫反射材质颜色设置为 alpha 分量为 0 的深蓝色。

每个顶点光照的着色器代码

[编辑 | 编辑源代码]

正如在 “平滑镜面高光”部分 中所讨论的那样,镜面高光通常使用每个顶点光照渲染得不好。但是,有时由于性能限制,别无选择。为了在 “光照纹理表面”部分 的着色器代码中包含光泽映射,两个通道的片段着色器都应该用这段代码替换

         #ifdef FRAGMENT
 
         void main()
         {
            vec4 textureColor = 
               texture2D(_MainTex, vec2(textureCoordinates));
            gl_FragColor = vec4(diffuseColor * vec3(textureColor)
               + specularColor * (1.0 - textureColor.a), 1.0);
         }
 
         #endif

请注意,通常的光泽贴图需要乘以 textureColor.a,而不是 (1.0 - textureColor.a)

恭喜!你完成了关于光泽映射的重要教程。我们已经了解了

  • 什么是光泽映射。
  • 如何为逐像素光照实现它。
  • 如何为每个顶点光照实现它。

进一步阅读

[编辑 | 编辑源代码]

如果你还想了解更多


< GLSL 编程/Unity

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