本系列主要参考《Unity Shaders and Effects
Cookbook》一书(感谢原书作者),同时会加上一点个人理解或拓展。
这里是本书所有的插图。这里是本书所需的代码和资源(当然你也可以从官网下载)。
========================================== 分割线 ==========================================
上一篇中,我们演示了如何使用自定义的光照模型进行渲染。这一次,我们将进一步看一下怎样对它做一些变化来得到更好的效果!
我们会列出两种方法:使用Half Lambert lighting model(半兰伯特光照模型)和使用一个ramp texture来控制diffuse
shading。
准备工作
Shader "Custom/BasicDiffuse" {
Properties {
_EmissiveColor ("Emissive Color", Color) = (1,1,1,1)
_AmbientColor ("Ambient Color", Color) = (1,1,1,1)
_MySliderValue ("This is a Slider", Range(0,10)) = 2.5
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf BasicDiffuse //We need to declare the properties variable type inside of the
//CGPROGRAM so we can access its value from the properties block.
float4 _EmissiveColor;
float4 _AmbientColor;
float _MySliderValue; struct Input
{
float2 uv_MainTex;
}; void surf (Input IN, inout SurfaceOutput o)
{
//We can then use the properties values in our shader
float4 c;
c = pow((_EmissiveColor + _AmbientColor), _MySliderValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
} inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
float difLight = max(0, dot (s.Normal, lightDir));
float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
col.a = s.Alpha;
return col;
} ENDCG
}
FallBack "Diffuse"
}
创建一个Half Lambert lighting model(半兰伯特光照模型)
Lambert最初也是被用于游戏半条命的画面渲染,为了防止某个物体的背光面丢失形状并且显得太过平面化。这个技术是完全没有基于任何物理原理的,而仅仅是一种感性的视觉增强(参考这里)。
inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
float difLight = max(0, dot (s.Normal, lightDir));
// Add this line
float hLambert = difLight * 0.5 + 0.5; float4 col;
// Modify this line
col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
col.a = s.Alpha;
return col;
}
由代码可以看出,我们定义了一个新的变量hLambert来替换difLight用于计算某点的颜色值。difLight的范围是0.0-1.0,而通过hLambert,我们将结果由0.0-1.0映射到了0.5-1.0,从而达到了增加亮度的目的。下图显示了这一变化:



创建一个ramp texture来控制diffuse shading
inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
float difLight = max(0, dot (s.Normal, lightDir));
float hLambert = difLight * 0.5 + 0.5;
float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb; float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
col.a = s.Alpha;
return col;
}
其中,我们还需要一张texture,即_RampTex。即之前说到的渐变图。回忆一下,为了能够在Inspector中拖拽一个texture,并在shader中使用需要怎么做?首先,我们需要在Properties块中声明它,然后在SubShader中声明一个相同名字的变量,并制定它的类型,之后就可以在函数中访问它啦!忘记的请翻看之前的几篇文章。完整的代码如下:
Shader "Custom/RampDiffuse" {
Properties {
_EmissiveColor ("Emissive Color", Color) = (1,1,1,1)
_AmbientColor ("Ambient Color", Color) = (1,1,1,1)
_MySliderValue ("This is a Slider", Range(0,10)) = 2.5
// Add this line
_RampTex ("Ramp Texture", 2D) = "white"{}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200 CGPROGRAM
#pragma surface surf BasicDiffuse //We need to declare the properties variable type inside of the
//CGPROGRAM so we can access its value from the properties block.
float4 _EmissiveColor;
float4 _AmbientColor;
float _MySliderValue;
// Add this line
sampler2D _RampTex; struct Input
{
float2 uv_MainTex;
}; void surf (Input IN, inout SurfaceOutput o)
{
//We can then use the properties values in our shader
float4 c;
c = pow((_EmissiveColor + _AmbientColor), _MySliderValue);
o.Albedo = c.rgb;
o.Alpha = c.a;
} inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
float difLight = max(0, dot (s.Normal, lightDir));
float hLambert = difLight * 0.5 + 0.5;
// Add this line
float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb; float4 col;
// Modify this line
col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
col.a = s.Alpha;
return col;
} ENDCG
}
FallBack "Diffuse"
}

float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb;
这行代码返回一个rgb值。tex2D函数接受两个参数:第一个参数是操作的texture,第二个参数是需要采样的UV坐标。这里,我们并不像使用一个vertex来代表一个UV坐标进行采样,而仅仅想使用一个漫反射浮点值(即hLambert)来映射到渐变图上的某一个颜色值。最后得到的结果便是,我们将会根据计算得到的Half Lambert光照值来决定光线照射到一个物体表面的颜色变化。
我们再来对比看一下Half Lambert和添加了ramp texture控制后的渲染区别(分别对应左图和右图):

