Phong冯氏光照模型
在现实生活当中,光照是一个复杂的过程,在计算机当中模拟这样的光照是非常复杂的
我们可以用一种简化的模型来对现实世界进行近似的光照模拟
Phong冯氏光照就是其中的一种比较简单的光照模型,
它是一个关于模型表面上点的局部照明的经验模型(empirical model)
由
发明
一个标准的Phong模型由以下三个部分组成
- Ambient(环境光)
- Diffuse(漫反射)
- Specular(高光/镜面反射)
Ambient(环境光)
在生活当中,即使是黑暗的环境当中,也存在一些光照,例如星光、月光等,环境光就是模拟这些光照效果,物体一般来说不可能是绝对的黑暗,所以在Phong模型当中,我们通常用一个ambient系数乘上一个颜色,来模拟环境光
float ambientStrength=0.1;//环境光的强度系数
vec3 ambient = ambientStrength * lightColor;//环境光
Diffuse(漫反射)
在我们生活当中光线照射到物体表面,物体越靠近光源的面其亮度系数也就越高
漫反射能表现物体的方向性,这也是Phong当中效果最显著的部分
为了计算光源的方向与亮度的关系,我们就要引入法线,通过入射光与法线直线的角度关系,得到光照的强度
下面是具体的计算代码
//计算出法向量和光线方向的单位向量
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
//判断漫反射强度系数
float diff = max(dot(norm, lightDir), 0.0);
//计算漫反射光照
vec3 diffuse = diff * lightColor;
首先要对计算的法向量和入射光向量,进行归一化,
将两个向量进行点积操作,并且限制在0~1之间得到光照的亮度系数
最后亮度系数乘上颜色即可
Specular(高光/镜面反射)
在现实生活中,一些光滑的物体除了基本的漫反射,还有高光的部分
光滑物体表面的高光,会随着人眼的视线发生改变,它依赖与人的观察方向
所以,我们就需要引入反射光向量与摄像机视角向量
$ \vec{R} $代表光线的反射光线。 人的视线与反射光线的夹角θ越小,观察到的镜面反射的效果越明显
float specularStrength = 0.5; //镜面反射强度
vec3 viewDir = normalize(viewPos - FragPos); //视线方向
vec3 reflectDir = reflect(-lightDir, norm); //反射光线
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor; //镜面反射光线
使用reflect函数得到反射光向量,reflect函数的推导过程可以参考的另外一篇
![]()
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);中的32代表高光的反射度(Shininess)。反射度越高,反射光的能力越强,高光点越小,如下图所示。
Blinn Phong(布林冯光照模型)
Phong的主要问题在于视线方向和反射方向之间的夹角必须小于90度,才能使镜面光项非零
V和R之间的角度大于90度。 Phong 没有正确模拟这样的情况。
在面向相机的点可能有微面,但 Phong 无法正确建模。
问题在于观察方向和反射方向之间的点积可能为负,这在通过方程的其余部分时不会导致合理的结果。
关于Phong模型中反射和视角大于90度的问题,可以通过改变计算方式来解决
这个修改后的模型称为Blinn-Phong高光模型或者仅仅是Blinn高光模型
它在物理上并不比Phong模型更正确。但它确实比Phong模型更加全面
Blinn 模型使用一组不同的向量进行计算,这些向量在所有有效情况下都小于 90 度
Blinn 模型需要计算半角向量。 半角矢量是视图方向和光位置之间的中间方向
计算半角向量
Blinn模型当中,使用入射光$\vec{L}$与摄像机视角$\vec{V}$,相加得到半角向量$\vec{H}$
即$ \vec{H} = \vec{L}+\vec{V} $
再用求得的半角向量$\vec{H}$与法向量$\vec{N}$进行点积运算,求得亮度系数
记得都要归一化哦!并且要限制在0~1之间
下面是在Unity当中实现的BlinnPhong
Shader “ShaderLearning/ShaderLearning”
{
Properties
{
_Shininess(“Shininess”,Range(1,64))=1
}
SubShader
{
Tags {“LightMode”=“ForwardBase”}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include “UnityCG.cginc”
#include “Lighting.cginc”
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
};
float _Shininess;
v2f vert (appdata_full v)
{
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
//计算世界坐标系下的光向量
float3 L=normalize(WorldSpaceLightDir(v.vertex));
//物体坐标系法线转为世界坐标系法线
float3 N=UnityObjectToWorldNormal(v.normal);
//计算世界空间下归一化的的摄像机观察方向
float3 V=normalize(_WorldSpaceCameraPos-mul(unity_ObjectToWorld,v.vertex));
//计算漫反射
float diffuse=saturate(dot(L,N));
//计算镜面高光,使用半角向量
float specular=pow(saturate(dot(N,normalize(L+V))),_Shininess);
//光源颜色分别和漫反射和镜面高光的强度系数相乘,最终赋给颜色
o.color=_LightColor0*(diffuse+specular);
return o;
}
fixed4 frag (v2f i) : COLOR
{
//片远着色器,最终的颜色加上固定的环境光颜色
return i.color+UNITY_LIGHTMODEL_AMBIENT;
}
ENDCG
}
}
}
Phong与Blinn Phong的效果对比
左边Phong冯氏光照模型效果
右边BlinnPhong 布林冯光照模型效果


...