Phong模型提出了計算鏡面高光的經驗模型,鏡面反射光強與反射光線和視線的夾角a相關:
Ispecular = Ks*Is*(cos a) n
其中
Ks表示物體表面的高光係數
Is表示光強
a表示反射光與視線的夾角
n表示高光指數(注意n爲冪指數,非乘數):n越大,則表面越光滑,反射光越集中,高光範圍越小
定義向量N爲法線向量,向量L爲入射光向量(頂點指向光源),向量R爲反射光向量(頂點指向反射方向),向量V爲視點向量(頂點指向視點方向),如下圖所示:
其中,公式中的cos a可表示爲V和R的點積,於是模型公式變爲:
Ispecular = Ks*Is*(V●R) n
向量V是已知的,只需要求出向量R即可。 反射光向量R可以通過入射光向量L和頂點法向量N求出:R = (2L●N)N – L
推到過程如下:
以向量L的終點爲起點,向量R的終點爲終點得到向量2P,由向量減法定義可知
2P=R-L (等式1)
同理根據向量減法從圖中可知
P=S-L (等式2)
其中S爲向量L在向量N上的投影,根據向量的投影公式可知
S=(L●N)●N / |N|2 (等式3)
由於此處所使用的向量都爲單位向量,於是上述公式可簡化爲
S=(L●N)●N (等式4)
將等式4代入等式2之後獲得
P=(L●N)●N-L (等式5)
將等式5代入等式1之後獲得
2((L●N)●N-L)=R-L
R=2(L●N)●N-L
接着我們來看下Shader中關於Phong光照函數的具體實現就會豁然開朗:
inline fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
{
// 通過計算反射向量R的公式(R = (2L*N)N – L)來計算出反射向量reflectionVector
float diff = dot(s.Normal, lightDir);
float3 reflectionVector = normalize((2.0 * s.Normal * diff) - lightDir);
// 將反射向量與視點向量進行點乘得到cos a,然後對cos a求specpower次方
float spec = pow(max(0,dot(reflectionVector, viewDir)), _SpecPower);
// 乘以高光顏色,即公式中的高光係數Ks
float3 finalSpec = _SpecularColor.rgb * spec;
fixed4 c;
// 下面公式中的加號前半部分爲漫反射光強,加號後半部分爲phong鏡面反射光強,此處又乘以光源顏色即公式中的光強係數Is
c.rgb = (s.Albedo * _LightColor0.rgb * diff) + (_LightColor0.rgb * finalSpec);
c.a = 1.0;
return c;
}