现实中光照是一件非常复杂的事情,人眼能看到的某个物体是因为该物体反射了光进入了人的眼晴,而物体反射光的来源并不单单只来源于光源,也包含其他物体反射出来的光,也就是说光线是在物体之间反复弹射(举个例子:光滑的瓷砖表面会折射出其他物体),除此之外,光滑的物体还会使人眼接收到更亮高光效果,所以说想要达到更加逼真的光照效果单靠Lambert光照模型模拟的漫反射效果是远远不够的。
原理参考资料: 现代计算机图形学入门-闫令琪 第8讲 (说明图片来源该课件截图)
Phong光照模型
\(L=L_{a}+L_{d}+L_{s}\)
即: 光照最终结果=环境光照+漫反射光照+镜面反射光照
1.环境光
由于\(L_{a}\)过于复杂(涉及到光照追踪),在该模型中被直接简化成\(L_{a}=k_{a}I_{a}\),即一个光照强度乘以一个常量,目的只有一个,让一个没有受到光源照射的物体不会那么黑(有个默认颜色),这里可以参考之前Lambert光照模型里面的图片效果
这里解释一下,但凡我们能看到的物体都一定反射了光,哪怕它没有直接接受光源的光,它也会反射来自其他地方的光,就是上面所说的接收了其他物体反射出来的光
2.漫反射
直接参考前文所讲的Lambert光照模型
3.镜面反射(核心点)
如图紫色方块代表单位受光面积(受光点),向量L为光入射方向,向量R为反射方向,向量V为观察方向,观察上面可以发现,当向量R与向量V接近时(夹角趋向0),人眼会受到高光效果。所以可以得出以下公式
\(L_{s}=k_{s}(I/r^{2})\max(0,\vec{R}\cdot\vec{V})^{p}\)
(衰减与系数参考Lamert光照模型)
指数系数p的作用:

在现实生活中,高光一般只存在于光的反射方向与观察方向夹角趋于0时才会有,稍微偏一点角度都不会看到高光效果,上图展示了不同指数的变化曲线,可以发现当指数越大,曲线变化越剧烈,所以公式中的指数系数p,就是用来处理这个现象的
BlinnPhong光照模型
BlinnPhong光照模型是Phong的改进型,Phong模型中向量R的计算非常的耗时(光照是逐片元计算的)
通过上图观察,向量V与量向R之间的夹角可以转换成向量H与法线向量N的夹角
而\(\vec{H}=\vec{L}+\vec{V}\) (计算量直接变成两个相量相加)
所以Phong模型的公式可以优化成:
\(L_{s}=k_{s}(I/r^{2})\max(0,\vec{N}\cdot (\vec{L}+\vec{V}))^{p}\)
最后完整公式:
\(L=L_{a}+L_{d}+L_{s}\)
\(=k_{a}I_{a}+k_{d}(I/r^{2})\max(0,\vec{N}\cdot\vec{L})+k_{s}(I/r^{2})\max(0,\vec{N}\cdot (\vec{L}+\vec{V}))^{p}\)
UnityShader 实现代码如下:
Shader "Custom/BlinnPhongLight"
{
Properties
{
_MainTex("MainTex",2D) = "white"{}
_Gloss("Gloss",Range(8.0,256)) = 20
}
SubShader
{
LOD 100
Pass
{
Name "LambertLight"
Tags{"LightMode" = "ForwardAdd"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform float4 _LightColor0;
struct a2v
{
float4 model_vertex : POSITION;
float3 model_normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 clip_pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 world_pos : TEXCOORD1;
float3 world_normal : TEXCOORD2;
float4 objPos : TEXCOORD3;
};
sampler2D _MainTex;
fixed _Gloss;
v2f vert(a2v v)
{
v2f o;
o.uv = v.uv;
o.clip_pos = UnityObjectToClipPos(v.model_vertex);
o.world_normal = mul((float3x3)unity_ObjectToWorld, v.model_normal); //UnityObjectToWorldNormal(v.model_normal);
o.world_pos = mul(unity_ObjectToWorld, v.model_vertex).xyz;
o.objPos = v.model_vertex;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//顶点法线(世界空间)
fixed3 world_normal = normalize(i.world_normal);
//光入射方向(世界空间)
fixed3 world_light = _WorldSpaceLightPos0.xyz - i.world_pos.xyz;
fixed3 world_light_dir = normalize(world_light);
//观察方向
fixed3 world_view_dir = normalize(_WorldSpaceCameraPos.xyz - i.world_pos.xyz);
//距离光源的位置
float world_light_distance = length(world_light);
//衰減系数
float atten = 1 / pow(world_light_distance, 2);
//环境光(固定系数)
fixed3 ambientColor = UNITY_LIGHTMODEL_AMBIENT.rgb;
//漫反射
fixed3 diffuseColor = atten * _LightColor0.rgb * max(0, dot(world_normal, world_light_dir));
//镜面反射
fixed3 specularColor = atten * _LightColor0.rgb * pow(max(0, dot(world_normal, normalize(world_view_dir + world_light_dir)) ), _Gloss);
//最终光颜色
fixed3 lightColor = ambientColor + diffuseColor +specularColor;
//贴图颜色
fixed4 texture_color = tex2D(_MainTex, i.uv);
//叠加光颜色
fixed3 finalColor = texture_color.rgb * lightColor;
return fixed4(finalColor, 1.0);
}
ENDCG
}
}
}
最终效果如下:

文章评论