纹理其实可以用于存储任何表面属性。一种常见的用法就是使用渐变纹理来控制漫反射光照的结果。
Shader "MyShader/RampTexture"
{
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_RampTex("Ramp Tex",2D) = "white"{}
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 worldNormal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f
{
float4 pos :SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.worldNormal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
return o;
}
fixed4 frag(v2f i) :SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert,halfLambert)).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
Fallback "Specular"
}
halfLambert 的范围被映射到了 [0,1] 之间。之后,我们使用 halfLambert 来构建一个纹理坐标,并用这个纹理坐标对渐变纹理 _RampTex 进行采样。由于 _RampTex 实际就是一个一维纹理(它在纵轴方向上颜色不变),因此纹理坐标的u和v方向我们都使用了halfLambert。然后,把从渐变纹理采样得到的颜色和材质颜色_Color 相乘,得到最终的漫反射颜色。
效果,ramp0
我们使用一张从紫色调到浅黄色调的渐变纹理。
效果ramp1
使用的渐变纹理是从黑色逐渐向浅灰色靠拢,而且中间的分界线部分微微发红。
效果ramp2
渐变纹理中的色调通常是突变的,即没有平滑过渡,以此来模拟卡通中的阴影色块。
各个图
需要注意的是,我们需要把渐变纹理的 Wrap Mode 设为 Clamp 模式,以防止对纹理进行采样时由于浮点数精度而造成的问题。
理论上halfLambert 的范围被映射到了 [0,1] 之间,但是可能出现1.00001这样的值。如果使用 Repeat 模式就会舍弃整数保留小数,得到的值就是0.00001,对应了渐变图中的最左边的值,即黑色。此时就会出现高光区域反而有黑点的情况。
参考 我买的 unity shader 入门精要