半兰伯特光照模型
半兰伯特光照模型
为什么需要半兰伯特光照模型
在前面我们使用的是兰伯特光照模型,它有一个问题,在光照无法到达的区域,模型的外观通常是全黑的,没有任何明暗变化,这会使模型的背光区域看起来就像一个平面一样,失去了模型细节表现。实际上我们可以通过添加环境光来得到非全黑的效果,但即便这样仍然无法解决背光面明暗一样的缺点。为此,有一种改善技术被提出来,这就是半兰伯特(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, 1.0);
}
ENDCG
}
}
}
逐顶点高光反射计算
高光反射光照模型
Specular = 直射光 * pow ( max(cosθ, 0) , 高光的参数 )
θ:是反射光方向和视野方向的夹角
pow 是求次方,pow(x, y)是求x的y次方
由下可以看出,高光的参数值越大,函数值衰减的越快(后半部分有值的实际都为0,因为有max的控制),所以反射光方向和视野方向的夹角越大得到的高光反射值越接近0,而且衰减得特别快。
逐顶点的高光反射Shader实现
在逐顶点漫反射的基础上实现
Specular.shader
Shader "Shader Learning Siki/Lighting/Specular PerVertex"
{
Properties
{
_Diffuse("Diffuse Color", Color) = (1, 1, 1, 1)
_Specular("Specular Color", Color) = (1, 1, 1, 1)
_Gloss("Gloss", Range(8, 200)) = 10
}
SubShader
{
Pass
{
Tags{
"LightMode" = "ForwardBase"}
CGPROGRAM
//用于取得第一个直射光的颜色 _LightColor0
//用于取得第一个直射光的位置 _WorldSpaceLightPos0
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
fixed4 _Specular;
half _Gloss;
struct a2v
{
float4 vertex : POSITION; //告诉Unity把模型空间下的顶点坐标填充给vertex
float3 normal : NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v v)
{
v2f f;
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; //获取环境光
fixed3 normalDir = normalize( mul(v.normal, (float3x3)_World2Object));
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光
fixed3 diffuse = _LightColor0.rgb * max(dot( normalDir, lightDir), 0) * _Diffuse.rgb; //取得漫反射的颜色
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul( v.vertex, _World2Object).xyz); //不可以这样子用,会有问题
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(_Object2World, v.vertex).xyz);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( max(0, dot(reflectDir, viewDir)), _Gloss);
f.color = diffuse + specular + ambient;
return f;
}
fixed4 frag(v2f f) : SV_Target
{
return fixed(f.color, 1.0);
}
ENDCG
}
}
Fallback "Diffuse"
}
逐像素高光反射
Shader "Shader Learning Siki/Lighting/Specular Pixel Level"
{
Properties
{
_Diffuse("Diffuse Color", Color) = (1, 1, 1, 1)
_Specular("Specular Color", Color) = (1, 1, 1, 1)
_Gloss("Gloss", Range(8.0, 200)) = 20
}
SubShader
{
Pass
{
Tags{
"LightMode" = "ForwardBase"}
CGPROGRAM
//用于取得第一个直射光的颜色 _LightColor0
//用于取得第一个直射光的位置 _WorldSpaceLightPos0
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
fixed4 _Specular;
half _Gloss;
struct a2v
{
float4 vertex : POSITION; //告诉Unity把模型空间下的顶点坐标填充给vertex
float3 normal : NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v)
{
v2f f;
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormal = mul(v.normal, (float3x3)_World2Object);
f.worldPos = mul(_Object2World, v.vertex).xyz;
return f;
}
fixed4 frag(v2f f) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; //获取环境光
fixed3 normalDir = normalize(f.worldNormal);
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光
fixed3 diffuse = _LightColor0.rgb * max(dot( normalDir, lightDir), 0) * _Diffuse.rgb; //取得漫反射的颜色
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( saturate(dot(reflectDir, viewDir)), _Gloss);
fixed3 color = diffuse + specular + ambient;
return fixed4(color, 1.0);
}
ENDCG
}
}
Fallback "Diffuse"
}
Blinn-Phong光照模型
在之前我们使用的是Phong光照模型
高光反射还有一种模型Blinn-Phong光照模型
Specular = 直射光 * pow ( max(cosθ, 0) , 高光的参数 )
θ:是光源方向和视野方向的夹角的平分线
Shader "Shader Learning/06BasicLighting/Specular BlinnPhong Pixel Level"
{
Properties
{
_Diffuse("Diffuse Color", Color) = (1, 1, 1, 1)
_Specular("Specular Color", Color) = (1, 1, 1, 1)
_Gloss("Gloss", Range(8.0, 200)) = 20
}
SubShader
{
Pass
{
Tags{
"LightMode" = "ForwardBase"}
CGPROGRAM
//用于取得第一个直射光的颜色 _LightColor0
//用于取得第一个直射光的位置 _WorldSpaceLightPos0
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
fixed4 _Specular;
half _Gloss;
struct a2v
{
float4 vertex : POSITION; //告诉Unity把模型空间下的顶点坐标填充给vertex
float3 normal : NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v)
{
v2f f;
f.position = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormal = mul(v.normal, (float3x3)_World2Object);
f.worldPos = mul(_Object2World, v.vertex).xyz;
return f;
}
fixed4 frag(v2f f) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; //获取环境光
fixed3 normalDir = normalize(f.worldNormal);
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //对于每个顶点来说 光源的位置就是光源的方向,因为光是平行光
fixed3 diffuse = _LightColor0.rgb * max(dot( normalDir, lightDir), 0) * _Diffuse.rgb; //取得漫反射的颜色
fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos);
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow( saturate(dot(normalDir, halfDir)), _Gloss);
fixed3 color = diffuse + specular + ambient;
return fixed4(color, 1.0);
}
ENDCG
}
}
Fallback "Diffuse"
}