Phong
Phong光照理论:物体表面反射光由三部分组成:环境光,漫反射,镜面反射(高光)。
漫反射的计算即Lambert光照模型,镜面反射的计算如下。
与Lambert公式类似,不同之处在于,这里和法线点乘的向量有入射光方向变成了视线方向(人直接看到的地方才高亮),并加以光泽度约束(实际约束了高光部分的范围,越小高光区域越大)。
Shader "Custom/Phong"
{
Properties
{
_MainColor("MainColor",Color) = (1, 1, 1, 1)
_Shininess("Shininess",Range(1,100)) = 1
_SpecularColor("Specular Color",Color) = (0, 0, 0, 0)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
struct v2f
{
float4 pos : SV_POSITION;
fixed4 color : COLOR0;
};
fixed4 _MainColor;
fixed4 _SpecularColor;
half _Shininess;
v2f vert (appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//法线向量 由模型空间变换到世界空间并标准化
float3 n = UnityObjectToWorldNormal(v.normal);
n = normalize(n);
//灯光法向向量
fixed3 l = normalize(_WorldSpaceLightPos0.xyz);
//视线方向向量
fixed3 view = normalize(WorldSpaceViewDir(v.vertex));
//漫反射
fixed ndotl = dot(n,l);
fixed4 dif = _LightColor0 * _MainColor * saturate(ndotl);
//镜面反射 光线方向取负,输入Reflect函数为光源到顶点的方向
float3 ref = reflect(-l, n);
ref = normalize(ref);
fixed rdotv = saturate(dot(ref,view));
fixed4 spec = _LightColor0 * _SpecularColor * pow(rdotv, _Shininess);
//环境光 + 漫反射 +镜面反射
o.color = unity_AmbientSky + dif + spec;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return i.color;
}
ENDCG
}
}
}
Blinn-Phong
为减少计算量,将Phong光照模型中的v替换成半角向量h(视角方向和灯光方向角平分方向),视觉效果差距不大,但当观察者和灯光离物体很远时镜面反射近似为常数。
半角向量计算公式:
Blinn-Phong 镜面反射计算公式:
修改镜面反射部分代码。
fixed4 frag (v2f i) : SV_Target
{
//法线向量 由模型空间变换到世界空间并标准化
float3 n = UnityObjectToWorldNormal(i.normal);
n = normalize(n);
//灯光法向向量
fixed3 l = normalize(_WorldSpaceLightPos0.xyz);
//视线方向向量
fixed3 view = normalize(WorldSpaceViewDir(i.vertex));
//漫反射
fixed ndotl = dot(n,l);
fixed4 dif = _LightColor0 * _MainColor * saturate(ndotl);
//镜面反射 光线方向取负,输入Reflect函数为光源到顶点的方向
float3 h = normalize(l + view);
fixed ndoth = saturate(dot(h, n));
fixed4 spec = _LightColor0 * _SpecularColor * pow(ndoth, _Shininess);
//环境光 + 漫反射 +镜面反射
return unity_AmbientSky + dif + spec;
}