Cg Programming/Unity/Glossy Textures光泽纹理

来源:互联网 发布:iphone搞怪视频软件 编辑:程序博客网 时间:2024/06/06 15:34

这里写图片描述从国际空间站(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)。

总结

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

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

扩展阅读

  • 关于逐像素光照(没有纹理),你应该阅读章节“光滑镜面高光”。
  • 关于纹理映射,你应该阅读章节“纹理球体”。
  • 关于纹理映射的逐顶点光照,你应该阅读章节“光照纹理表面”。
原创粉丝点击