5.4 阴影与全局光照:光路不止一棒


5.4 阴影与全局光照:光路不止一棒

本节摘要:Phong 模型只算"灯到表面"一棒光路,阴影与反弹光的缺席让画面失真。本节讲阴影图(shadow mapping)的两遍渲染机制及其两大病症的修法,再梳理全局光照的谱系:从光照贴图烘焙、屏幕空间技术到光线追踪级方案,划清实时与离线的边界。它是第 5 站的终点,也是第 7 章离线渲染的起跳板。

读完这节你该会什么

阅读完本节,你应当能够:

  1. 描述阴影图两遍渲染的完整流程,写出门深度的比较判断
  2. 诊断阴影痤疮与彼得潘失真并给出对应修法
  3. 区分硬阴影与软阴影的产生机制
  4. 列出全局光照方案的谱系与各自的真实性/成本档位

阴影:从"点亮一切"说起

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)——近平面到远平面分几张阴影图各管一段,本质还是本节的矩阵与比较,只是多了一层"按距离选图"。

本节要点回顾

  • 阴影图两遍渲染:灯当相机画深度,正式渲染比深度,复用度高
  • 痤疮与彼得潘:bias 调小长痤疮、调大丢影子,永恒的平衡
  • PCF 平均出软边:物理正确的半影要等光源面积采样
  • GI 谱系按成本排开:烘焙、探针、屏幕空间、光追、路径追踪
  • 渲染方程是终极账本:半球积分定义了"完全正确的图",一切算法是它的近似

第 5 站到站。像素有了位置、深度、亮度和皮肤,接下来把整条生产流程装上流水线——第 6 章,渲染管线与 GPU。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U