半兰伯特光照模型
半兰伯特光照模型
为什么需要半兰伯特光照模型
在前面我们使用的是兰伯特光照模型,它有一个问题,在光照无法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,这会使模型的背光区域看起来就像一个平面一样,失去了模型细节表现。实际上我们可以通过添加环境光来得到非全黑的效果,但即便这样仍然无法解决背光面明暗一样的缺点。为此,有一种改善技术被提出来,这就是半兰伯特(Half Lambert)光照模型。
半兰伯特光照模型公式
广义的半兰伯特光照模型的公式如下:
c_diffuse=(c_light∙m_diffuse)(α(n ̂⋅l ̂ )+β)
可以看出,与原兰伯特模型相比,半兰伯特光照模型没有使用max操作来防止n ̂和l ̂的点积为负值,而是对其结果进行了一个α倍的缩放再加上一个β大小的偏移。绝大多数情况下,α和β的值均为0.5,即公式为:
c_diffuse=(c_light∙m_diffuse )(“0.5” (n ̂⋅l ̂ )+“0.5” )
通过这样的方式,我们可以把n ̂⋅l ̂的结果范围从[-1, 1]映射到[0, 1]范围内。也就是说,对于模型的背光面,在原兰伯特光照模型中点积结果将映射到同一个值,即0值处;而在半兰伯特模型中,背光面也可以有明暗变化,不同的点积结果会映射到不同的值上。
需要注意的是,半兰伯特是没有任何物理依据的,它仅仅是一个视觉加强效果。
由于n ̂⋅l ̂的值等于cosθ,因此我们可以查看(“0.5” (n ̂⋅l ̂ )+“0.5” )的函数图像,这样比较清晰明了。
Shader编写
DiffuseFragment_HalfLambert.shader
Shader "Shader Learning Siki/Lighting/Diffuse PerFragment Half Lambert"
{
Properties
{
_Diffuse("Diffuse Color", Color) =(1.0, 1.0, 1.0, 1.0)
}
SubShader
{
Pass
{
Tags{
"LightMode" = "ForwardBase" } //这里一定不要忘记定义了,要定义合适的LightMode
CGPROGRAM
#include "Lighting.cginc"
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 worldNormal : COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 worldNormalize = normalize(i.worldNormal);
//fixed3 worldNormal = normalize(mul((float3x3)_Object2World, v.normal));
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光
fixed halfLambert = dot(worldNormalize, worldLight) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
fixed3 color = diffuse + ambient;
return fixed4(color