本节摘要:Phong 模型只算"灯到表面"一棒光路,阴影与反弹光的缺席让画面失真。本节讲阴影图(shadow mapping)的两遍渲染机制及其两大病症的修法,再梳理全局光照的谱系:从光照贴图烘焙、屏幕空间技术到光线追踪级方案,划清实时与离线的边界。它是第 5 站的终点,也是第 7 章离线渲染的起跳板。
阅读完本节,你应当能够:
5.1 的光照模型有个隐藏谎言:只要 L·N > 0,点就被照亮,哪怕中间隔着一堵墙。真实世界里墙后的物体在阴影里,因为光被墙挡住了。判断"灯与你之间有没有障碍",就是阴影计算的全部。
从灯的位置"再渲染一遍场景":用灯当相机做一次投影(4.2/4.3 的矩阵直接复用),得到的深度图记录"沿光的方向,最先撞到谁"。正式渲染时,把像素位置换算到灯的深度图坐标系,比较两个深度:像素比图上记录的更远,说明光被别人截胡,它在阴影里。
import numpy as np def in_shadow(px, py, pz, light_depth, bias=0.005): # px.. 像素在灯光坐标系下的位置;light_depth 为阴影图 u, v = int(px*32), int(py*32) # 简化:直接当地图坐标 if not (0 <= u < 32 and 0 <= v < 32): return False # 灯外区域视为照亮 return pz > light_depth[v, u] + bias # 比最近遮挡者远 → 在影中 light_depth = np.full((32, 32), 9.9) # 灯深度图:默认无遮挡 light_depth[10:14, 10:14] = 2.0 # 光路上 2 米处立了一块板 print(in_shadow(0.4, 0.4, 3.0, light_depth), # 板后方 3 米处 in_shadow(0.4, 0.4, 1.5, light_depth), # 板前方 1.5 米处 in_shadow(0.9, 0.9, 3.0, light_depth)) # 板旁边 # 输出: True False False 两遍信息一比较,谁被截胡一目了然
两遍渲染高度复用已有管线(换个相机再画一遍深度),硬件友好,是实时阴影的绝对主流。但它有两个著名病症:其一,阴影痤疮——表面自遮挡的深度比较在数值边缘抖动,阴影里长满条纹噪点,修法是深度偏移 bias 与法线偏移把比较往宽容方向推;其二,彼得潘失现——bias 推过头,物体"飘"起来、脚下漏光,像童话里丢了影子的小飞侠。两个病症共享一个调参旋钮,工程上永远在两者之间找平衡,或改用更稳的方案(如把深度比较换成距离场)。
阴影图的比较是二值的,边缘锐利——但真实阴影边缘随距离变糊(本影与半影:光源越大、遮挡物离影面越远,边缘越软)。实时的近似是 PCF:在阴影图上取多个样本平均比较结果,边缘出现渐变;更精细的 PCSS 按遮挡距离自适应模糊半径。物理正确的软阴影要到 7.1 的光线追踪里对光源面积采样才算数。
Phong 的环境光项把所有间接弹射压成一个常数,这是一个多么粗糙的压缩,站在彩色房间里的白墙就明白:地面会泛起墙色的反光(颜色渗透),红色墙纸把白桌染成粉的。这类"光弹射多次"的现象统称全局光照(GI),方案按成本排开:
| 方案 | 思路 | 成本 | 典型用途 |
|---|---|---|---|
| 光照贴图 | 离线烘焙静态场景的弹射光到贴图 | 一次离线 | 静态关卡游戏 |
| 球谐/探针 | 预算周围环境的入射光概略,运行时查询 | 低 | 移动端角色环境光 |
| 屏幕空间 GI | 只利用当前帧已有信息近似弹射 | 中 | 主机游戏的补充 |
| 光线追踪 GI | 对弹射路径实际追踪 | 高 | 离线渲染、RTX 场景 |
| 路径追踪 | 蒙特卡洛积分求解完整渲染方程 | 极高 | 影视、动画电影 |
渲染方程的直觉表述: 出射光 = 自发光 + 反射率 × ∫(入射光 × 入射余弦 × 双向分布函数)d入射方向 积分域是围绕法线的整个半球——所有可能来光的方向 1.3 节埋的积分伏笔在此兑现:实时用几个方向近似,离线用路径追踪采样
光照贴图是最划算的一档:静态场景的间接光离线算好烤进贴图,运行时几乎白送;代价是动态物体不参与烘焙,角色在烘焙过的大厅里会显得"与环境光脱节",需要探针类技术补缝。光线追踪与路径追踪的完整机制留给第 7 章,这里先立起"局部/全局"这条分界线:它就是实时与离线两个世界的主要边界。
💡 关键直觉:看一幅画面判断 GI 档次的快速方法——盯住彩色墙面旁边的白色物体。如果白物上泛着墙色,说明至少有烘焙级 GI;如果纯白不变,多半只有环境光常数在撑场面。
背景:某室内场景,角色阴影在自己的脸上出现密集条纹(阴影痤疮)。操作:第一步把深度偏移从 0.001 逐步加大,0.005 时脸部干净但脚下开始漏光(彼得潘);第二步改用法线偏移——沿表面法线把采样点外推一小段,脸部干净且漏光更轻;第三步把阴影图分辨率从 512 提到 2048,配合 3×3 PCF 采样,边缘也变得柔和。结果:三级手段叠加后阴影稳定。解读:痤疮源于阴影图精度不足导致的深度比较抖动,三招分别从"放宽比较""挪开采样点""提高精度"三个方向减压,工程里几乎总是组合使用。变式:若场景是超大户外地形,2048 阴影图盖不过来,就上级联阴影(CSM)——近平面到远平面分几张阴影图各管一段,本质还是本节的矩阵与比较,只是多了一层"按距离选图"。
第 5 站到站。像素有了位置、深度、亮度和皮肤,接下来把整条生产流程装上流水线——第 6 章,渲染管线与 GPU。