Cg Programming/Unity/Glossy Textures光泽纹理

这里写图片描述从国际空间站(ISS)看到的太平洋上空带有镜面高光的日没。

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

译者注:通过普通的镜面高光(specular light)方程,可以使得模型在某个角度看起来具有光泽。 但是有时候我们想使得模型的高光区域是不规则的。您可以使用光泽贴图(Gloss Map)控制反射高光显示位置。指定给光泽度材质组件的贴图决定整个曲面的哪些区域更有光泽,哪些区域不太有光泽,具体情况取决于贴图中颜色的强度。贴图中的黑色像素将产生全面的光泽。白色像素将完全消除光泽,中间值会减少高光的大小。
光泽贴图是一张黑白纹理,我们使用这张纹理控制特定顶点的反射程度,黑白纹理让我们可以很容易地做到这点
扩展阅读

它结合章节“纹理球体”和“光滑镜面高光”中的着色器代码来计算带有一个材质颜色的逐像素光照,对于漫反射它是由纹理的RGB分量决定的,镜面反射的强度是由相同纹理的A分量决定的。如果你没有读过这些章节,这会是一个非常好的机会来了解它们。

光泽映射

在章节“光照纹理表面”中,漫反射的材质常量是由纹理贴图的RGB分量决定的。这里我们延伸一下这个技术,然后通过相同纹理贴图的A (alpha)分量来决定镜面反射的强度。只使用一张纹理就提供了显著的性能优势,特别是因为RGBA纹理查找代价在某种情况下跟RGB纹理查找同样昂贵(译者注:为什么昂贵可以参考以上的扩展阅读)。

如果纹理贴图的“光泽度”(即镜面反射的强度)被编码在RGBA纹理贴图的A (alpha)中,我们可以简单地把镜面反射材质常量这里写图片描述与纹理贴图的alpha分量进行相乘。这里写图片描述在章节“镜面高光”中介绍过,并且它出现在Phone反射模型的镜面反射项中:
这里写图片描述

如果乘以纹理贴图的alpha分量,这个值就会在alpha为1时达到最大(也就是表面是光泽的),在alpha为0时等于0(也就是表面没有任何光泽)。

逐像素光照代码

这里写图片描述带有透明水的地球地图,也就是水的alpha分量为0,陆地的alpha分量为1。

下面这段着色器代码结合了章节“光滑镜面高光”的逐像素光照和章节“纹理球体”中的纹理映射。跟章节“光照纹理表面”类似,在textureColor中纹理颜色的RGB分量乘以漫射材质颜色_Color。

在上图中,水的alpha分量为0,陆地的alpha分量为1。但是,水应该是有光泽的而陆地没有。因此,在这个特定的图片中,我们应该用(1.0 - textureColor.a)乘以镜面材质颜色。另一方面,通常光泽映射会需要跟textureColor.a相乘。(注意对于着色器编程来讲这种改变有多简单。)

Shader "Cg 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

         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 

         #include "UnityCG.cginc"
         uniform float4 _LightColor0; 
            // color of light source (from "Lighting.cginc")

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

         struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
        };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 posWorld : TEXCOORD0;
            float3 normalDir : TEXCOORD1;
            float4 tex : TEXCOORD2;
        };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;

            float4x4 modelMatrix = _Object2World;
            float4x4 modelMatrixInverse = _World2Object;

            output.posWorld = mul(modelMatrix, input.vertex);
            output.normalDir = normalize(
               mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
            output.tex = input.texcoord;
            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            float3 normalDirection = normalize(input.normalDir);

            float3 viewDirection = normalize(
               _WorldSpaceCameraPos - input.posWorld.xyz);
            float3 lightDirection;
            float attenuation;

            float4 textureColor = tex2D(_MainTex, input.tex.xy);

            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = 
                  normalize(_WorldSpaceLightPos0.xyz);
            } 
            else // point or spot light
            {
               float3 vertexToLightSource = 
                  _WorldSpaceLightPos0.xyz - input.posWorld.xyz;
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
            }

            float3 ambientLighting = textureColor.rgb  
               * UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;

            float3 diffuseReflection = textureColor.rgb  
               * attenuation * _LightColor0.rgb * _Color.rgb
               * max(0.0, dot(normalDirection, lightDirection));

            float3 specularReflection;
            if (dot(normalDirection, lightDirection) < 0.0) 
               // light source on the wrong side?
            {
               specularReflection = float3(0.0, 0.0, 0.0); 
                  // no specular reflection
            }
            else // light source on the right side
            {
               specularReflection = attenuation * _LightColor0.rgb 
                  * _SpecColor.rgb * (1.0 - textureColor.a) 
                     // for usual gloss maps: "... * textureColor.a" 
                  * pow(max(0.0, dot(
                  reflect(-lightDirection, normalDirection), 
                  viewDirection)), _Shininess);
            }

            return float4(ambientLighting + diffuseReflection 
               + specularReflection, 1.0);
         }

         ENDCG
      }

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

          CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 

         #include "UnityCG.cginc"
         uniform float4 _LightColor0; 
            // color of light source (from "Lighting.cginc")

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

        struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
        };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 posWorld : TEXCOORD0;
            float3 normalDir : TEXCOORD1;
            float4 tex : TEXCOORD2;
        };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;

            float4x4 modelMatrix = _Object2World;
            float4x4 modelMatrixInverse = _World2Object;

            output.posWorld = mul(modelMatrix, input.vertex);
            output.normalDir = normalize(
               mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
            output.tex = input.texcoord;
            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            float3 normalDirection = normalize(input.normalDir);

            float3 viewDirection = normalize(
               _WorldSpaceCameraPos - input.posWorld.xyz);
            float3 lightDirection;
            float attenuation;

            float4 textureColor = tex2D(_MainTex, input.tex.xy);

            if (0.0 == _WorldSpaceLightPos0.w) // directional light?
            {
               attenuation = 1.0; // no attenuation
               lightDirection = 
                  normalize(_WorldSpaceLightPos0.xyz);
            } 
            else // point or spot light
            {
               float3 vertexToLightSource = 
                  _WorldSpaceLightPos0.xyz - input.posWorld.xyz;
               float distance = length(vertexToLightSource);
               attenuation = 1.0 / distance; // linear attenuation 
               lightDirection = normalize(vertexToLightSource);
            }

            float3 diffuseReflection = textureColor.rgb  
               * attenuation * _LightColor0.rgb * _Color.rgb
               * max(0.0, dot(normalDirection, lightDirection));

            float3 specularReflection;
            if (dot(normalDirection, lightDirection) < 0.0) 
               // light source on the wrong side?
            {
               specularReflection = float3(0.0, 0.0, 0.0); 
                  // no specular reflection
            }
            else // light source on the right side
            {
               specularReflection = attenuation * _LightColor0.rgb 
                  * _SpecColor.rgb * (1.0 - textureColor.a) 
                     // for usual gloss maps: "... * textureColor.a" 
                  * pow(max(0.0, dot(
                  reflect(-lightDirection, normalDirection), 
                  viewDirection)), _Shininess);
            }

            return float4(diffuseReflection 
               + specularReflection, 1.0);
               // no ambient lighting in this pass
         }

         ENDCG
      }
   }
   Fallback "Specular"
}

对于以上特定的图像的着色器,一个有意义的修改就是在alpha分量为0时设置漫反射材质颜色为深蓝色。

逐顶点光照着色器

就像在章节“光滑镜面高光”中讨论的一样,用逐顶点光照的镜面高光通常渲染得不是很好。但是,有时因为性能上限制我们并没有其它选择。为了在章节“光照纹理表面”中的着色器包含光泽映射,两个通道的片元着色器应该被以下的代码替换:

float4 frag(vertexOutput input) : COLOR
         {
            float4 textureColor = tex2D(_MainTex, input.tex.xy);
            return float4(input.specularColor * (1.0 - textureColor.a) +
               input.diffuseColor * textureColor.rgb, 1.0);
         }

注意通常的光泽映射应该需要跟textureColor.a相乘而不是(1.0 - textureColor.a)。

总结

恭喜,你完成了关于光泽映射最重要一章的学习。我们学到了:

  • 什么是光泽映射
  • 如何用逐顶点光照实现光泽映射
  • 如何用逐像素光照实现光泽映射

扩展阅读

  • 关于逐像素光照(没有纹理),你应该阅读章节“光滑镜面高光”。
  • 关于纹理映射,你应该阅读章节“纹理球体”。
  • 关于纹理映射的逐顶点光照,你应该阅读章节“光照纹理表面”。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值