Phong光照模型
首先来看一下Phong光照模型的计算式:
Lo(v)=(cosθi∗cdiff+cosmαr∗cspec)⊗BL
其中,θi为入射光i和法线n的夹角(cosθi=−i⋅n),αr为反射光r和视线v的夹角(cosαi=−r⋅v),cdiff、cspec和m都是材质本身的属性(分别对应漫反射项颜色、高光项颜色以及反光度),BL是入射光的颜色,注意不是辐照度(irradiance)。
补充一下反射光方向的计算式:r=i−2(i⋅n)n

Phong光照模型示意图
Phong光照模型的BRDF版本
对于Phong光照模型的BRDF版本(Normalized Phong),入射光的颜色BL与辐照度EL的关系为BL=πEL。引入修正系数,计算式为(此处推导见BRDF·基于物理的着色技术学习总结):
Lo(v)=∑kf(ik,v)⊗ELkcosθik
其中双向反射分布函数(BRDF)为:
f(i,v)=πcdiff+2π(m+2)cspec∗cosmαr
Blinn-Phong光照模型
Blinn-Phong模型与Phong模型的不同之处在于高光项的夹角计算方式。看一下Blinn-Phong光照模型的计算式:
Lo(v)=(cosθi∗cdiff+cosmβh∗cspec)⊗BL
其中,θi为入射光i和法线n的夹角(cosθi=−i⋅n),βh为半角向量h和法线n的夹角(cosβh=h⋅n),cdiff、cspec和m都是材质本身的属性(分别对应漫反射项颜色、高光项颜色以及反光度),BL是入射光的颜色,同样注意不是辐照度(irradiance)。
补充一下半角向量的计算式:h=−∥i+v∥i+v

Blinn-Phong光照模型示意图
Blinn-Phong光照模型的BRDF版本
对于Blinn-Phong光照模型的BRDF版本(Normalized Blinn-Phong),引入修正系数。计算式为(此处只列出BRDF的计算式,推导见BRDF·基于物理的着色技术学习总结):
f(i,v)=πcdiff+8(2−2m+m)π(m+2)(m+4)cspec∗cosmαr
或近似为
f(i,v)=πcdiff+8π(m+8)cspec∗cosmαr
两种模型的效果与优劣
首先对比两种模型的效果:

两种模型的效果图1
第一张效果图中,我们可以发现使用Blinn-Phong模型时高光更亮。

两种模型的效果图2,此处可以发现Phong模型的一个问题
第二张效果图中,我们可以发现Phong模型在一些情况下会导致显示效果不平滑。当反射光与视线的夹角大于90度时,由于在实际渲染时余弦值总是被限制在[0,1],在这些地方上高光项没有任何贡献。当物体的反光度非常小时,高光半径就会非常大,在反射角与视线夹角为90度的区域附近就会出现很明显的断层现象。而由于半角向量与法线夹角总不大于90度,使用Blinn-Phong模型时断层现象不会发生。

效果图2的断层问题的产生原因示意图
可能的误区
通常在讲Blinn-Phong模型的优势时,都会提到Blinn-Phong模型的计算比Phong更高效。在Real-Time Rendering中提到,如果入射方向和视线方向保持不变,半角向量可以只计算一次并重用。此外,半角向量的计算比反射方向的计算量更少。
但是,回顾两个向量的计算方式:
r=i−2(i⋅n)n
h=−∥i+v∥i+v
由于入射方向和法线方向都是单位向量,求反射方向需要数次乘法和加法,求半角向量需要数次加法和一次标准化。那么数次加法和一次标准化真的比数次乘法和加法快吗?
这里我们认为快和慢取决于具体的硬件平台,因为这些操作都有可能有相应的硬件指令,以加快一些常用运算。现代GPU通常支持向量点乘指令、平方根倒数指令,甚至支持归一化指令。实际上速度差距并不大。
以上结果反映Blinn-Phong的优势主要还是高光效果更平滑。
参考资料
Real-Time Rendering (3rd Edition)
BRDF·基于物理的着色技术学习总结
https://learnopengl.com/Advanced-Lighting/Advanced-Lighting
为什么说使用 halfway vector 可以提高效率? - Milo Yip的回答 - 知乎