1 漫反射
是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这种反射的光称为漫射光。
漫反射光照符合兰伯特定律
2 兰伯特光照模型
2.1 兰伯特光照公式
C d i f f u s e = ( C l i g h t ⋅ M d i f f u s e ) m a x ( 0 , d o t ( N ⃗ , L ⃗ ) ) C_{diffuse} = (C_{light} \cdot M_{diffuse})max(0, dot(\vec{N}, \vec{L})) Cdiffuse=(Clight⋅Mdiffuse)max(0,dot(N,L))
N ⃗ \vec{N} N是表面法线, L ⃗ \vec{L} L是光源方向的单位矢量, d o t ( N ⃗ , L ⃗ ) dot(\vec{N}, \vec{L}) dot(N,L)则是取两者的内积。 M d i f f u s e M_{diffuse} Mdiffuse是材质的漫反射颜色, C l i g h t C_{light} Clight是光源颜色。使用取最大值函数 m a x ( ) max() max()来将其截取到0,还可以防止物体被从后面来的光源照亮。
2.2 内积公式
d o t ( N ⃗ , L ⃗ ) = ∣ N ⃗ ∣ ⋅ ∣ L ⃗ ∣ ⋅ cos θ dot(\vec{N}, \vec{L})=|\vec{N}|\cdot|\vec{L}|\cdot\cos\theta dot(N,L)=∣N∣⋅∣L∣⋅cosθ
内积的几何意义是向量 N ⃗ \vec{N} N在向量 L ⃗ \vec{L} L上的投影,如果两者为单位向量,则内积即两向量夹角的余弦值。
3 半兰伯特光照模型
参考博客:https://www.cnblogs.com/xhg986/p/7510372.html
C d i f f u s e = ( C l i g h t ⋅ M d i f f u s e ) m a x ( 0 , d o t ( N ⃗ , L ⃗ ) ) × 0.5 + 0.5 C_{diffuse} = (C_{light} \cdot M_{diffuse})max(0, dot(\vec{N}, \vec{L})) \times 0.5 + 0.5 Cdiffuse=(Clight⋅Mdiffuse)max(0,dot(N,L))×0.5+0.5
即在兰伯特光照模型基础上乘以0.5再加上0.5
4 Unity Shader代码
逐像素实现要比逐顶点更加平滑
4.1 逐顶点实现
// 半兰伯特光照模型 (逐顶点实现)
Shader "LShaders/LambertVert"
{
Properties
{
_DiffuseColor("Diffuse Color", Color) = (1,1,1,1)
}
SubShader
{
Pass
{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL; // 顶点法线
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 color: COLOR;
};
v2f vert (appdata v)
{
v2f o;
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 世界空间下的法线
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
// 世界空间下光源方向
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// 半兰伯特光照模型:Clight * Mdiffuse * (dot(worldNormal, worldLightDir) * 0.5 + 0.5)
fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
// 漫反射 = 光源颜色 * 漫反射颜色 * (顶点法线和光源方向的点积,归一化)
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * halfLambert;
// 最终颜色 = 环境光 + 漫反射光
fixed3 color = ambient + diffuse;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = color;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.color, 1.0);
}
ENDCG
}
}
}
4.2 逐像素实现
// 半兰伯特光照模型 (逐像素实现)
Shader "LShaders/DiffuseHalfLambertLighting"
{
Properties
{
_DiffuseColor("Diffuse Color", Color) = (1,1,1,1)
}
SubShader
{
Pass
{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL; // 顶点法线
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 worldNormal: TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 世界空间下的法线
fixed3 worldNormal = i.worldNormal;
// 世界空间下光源方向
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// 半兰伯特光照模型:Clight * Mdiffuse * (dot(worldNormal, worldLightDir) * 0.5 + 0.5)
fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
// 漫反射 = 光源颜色 * 漫反射颜色 * (顶点法线和光源方向的点积,归一化)
fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * halfLambert;
// 最终颜色 = 环境光 + 漫反射光
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0);
}
ENDCG
}
}
}