在unity的Shader中使用半兰伯特模型来构建光照
在认识半兰伯特模型之前,建议查看一下我之前的兰伯特模型构建光照
由于兰伯特模型的计算公式仅截取了[0,1]的光照区间,因此可能会丢失掉阴影部分的暗部细节。而Valve公司在开发《半条命》的时候,提出了一个新的光照技术,该技术是有原兰伯特光照模型的基础上进行了一个简单的修改——因此被称为半兰伯特光照模型。广义的半兰伯特光照模型如下:
和兰伯特光照模型类似,只是将Max函数替换成了(a·(n·I)+b)这样一来就可以保留充足的暗部细节。通常情况下,a和b的值为0.5。即该公式为如下:
之所以能够保留住暗部细节,是因为就算 n·I 的值最大为1,也会被乘0.5,然后再加上0.5。仍然可以确保右边括号仍然大于0且小于1。意思是将所有点积结果缩小后,再整体放大0.5,最终的效果其实是偏亮的。因为就算是阴影部分,最后还是会被提高了0.5。
Shader "LeonShader/shader_6_4_Diffuse_HalfLambert"
{
Properties{
_Diffuse("Diffuse",Color) = (1,1,1,1)
}
SubShader{
Pass{
Tags { "LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v {
float4 vertex : POSITION;
float4 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
};
v2f vert(a2v v) {
v2f o;
//将定点左边从本地空间转变投影空间
o.pos = UnityObjectToClipPos(v.vertex);
//得到环境信息
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//法线信息由物体空间转变为世界空间
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
fixed3 color = ambient + diffuse;
return fixed4(color , 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}