Advanced Lighting 高级光照
LearnOpenGL 顺序:Advanced Lighting → Advanced Lighting · 原教程
这一节在渲染管线中的位置
这一节没有增加新的渲染阶段。它改的是 Fragment Shader 里“镜面高光怎么计算”。
以前我们用 Phong:
Light
↓
表面法线 N
↓ reflect()
反射方向 R
再比较:
R 和观察方向 V 有多接近镜面项:
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);这套方法大多数时候很好,但在 shininess 很低、高光区域很宽的时候会暴露一个问题:当 V 和 R 的夹角超过 90°,点积直接变成负数,经过 max(…, 0) 后整块镜面贡献突然变成 0,于是高光边缘会出现明显断层。
Blinn-Phong 为什么换成 Halfway Vector
Blinn-Phong 不再问:
“观察方向 V 和完美反射方向 R 有多接近?”而是问:
“法线 N 和 光线方向 L、观察方向 V 的中间方向 H 有多接近?”Halfway Vector:
vec3 halfwayDir = normalize(lightDir + viewDir);也就是:
H = normalize(L + V)直觉上可以这样想:
如果观察者正好站在镜面反射方向上:
N
↑
H ↑
/ \
L V
这时 H 会非常接近 N
→ 高光最强当观察方向逐渐偏离理想反射方向,H 也会逐渐偏离 N,高光平滑减弱。相比直接比较 V 和 R,Halfway Vector 在常见可见表面范围内不会那么容易跨过“90° → 点积突然归零”这个边界。
为什么 Blinn-Phong 的 shininess 通常要更大
Phong 比较:
dot(V, R)Blinn-Phong 比较:
dot(N, H)而 H 是 L 与 V 的中间方向,所以 N 和 H 的夹角通常比 V 和 R 的夹角更小。
因此如果两边都写相同的指数,Blinn-Phong 的高光通常会更宽。为了得到相近的高光宽度,教程通常让 Blinn-Phong 使用更高的指数。官方示例使用:
Phong shininess = 8
Blinn-Phong shininess = 32通常可以在 Phong 指数的 2~4 倍附近继续调。
Interactive Lab
Lab 会同时渲染同一个地面:
左:Phong
右:Blinn-Phong可以直接调整 shininess、相机角度、光源位置,并切换“相同指数 / 匹配高光宽度”。把 Phong shininess 降到很低时,最容易看到 Phong 镜面高光边缘的突然截断;再看 Blinn-Phong,会发现高光衰减更连续。
这一节真正要留下来的模型
真正变化只有镜面项的几何问题:
Phong
N + L
↓
反射方向 R
↓
比较 R 和 V
Blinn-Phong
L + V
↓
Halfway Vector H
↓
比较 H 和 N所以这节最重要的一句话是:
Phong 追踪“完美反射光线”;Blinn-Phong 追踪“光线与视线之间的中间方向”。
两者都还是经验光照模型;Ambient、Diffuse 的计算没有因为 Blinn-Phong 而改变。