第 7 讲:阴影与 Shadow Mapping

阴影不仅让画面更真实,还直接告诉人眼物体是否接触地面、谁在谁前面、彼此相距多远。点光源产生硬阴影;有面积的光源会形成全影与半影,边缘更软。

1. 阴影判断的本质

对一个表面点 PP,从 PP 向光源发射一条线段:

  • 中间没有几何体,PP 被照亮。
  • 中间先碰到别的几何体,PP 在阴影里。

光线追踪可直接做这次可见性查询。光栅化管线更常使用 Shadow Mapping,把问题改成一次深度图查询。

2. 两遍渲染

Shadow Mapping 两遍渲染

第一遍从光源看场景:

  1. 使用光源的视图矩阵 VLV_L 和投影矩阵 PLP_L。
  2. 正常光栅化所有可能投影阴影的物体。
  3. 只保存每个光源像素最近的深度,得到 Shadow Map。

第二遍从相机看场景:

  1. 正常计算相机画面中的片元。
  2. 用 PLVLMP_LV_LM 把当前点变换到光源裁剪坐标。
  3. 透视除法后,再把 [−1,1][-1,1] 映到纹理坐标 [0,1][0,1]。
  4. 取出 Shadow Map 深度 zmapz_{map},与当前点到光源的深度 zPz_P 比较。

若

zP>zmap+bias,z_P>z_{map}+\text{bias},

说明光源在到达 PP 之前先遇到了别的表面,PP 在阴影中。

3. 坐标矩阵为何容易写错

一个相机片元要被投回“光源眼中的纹理位置”,所以常见纹理矩阵包含:

Mtex PLVLM.M_{tex}\,P_LV_LM.

MtexM_{tex} 负责从 NDC 的 [−1,1][-1,1] 缩放平移到纹理坐标的 [0,1][0,1]。课件中的顶点着色器先生成光源空间位置,片元着色器再做深度比较。调试时可以直接显示 Shadow Map;若光源深度图本身不对,第二遍再怎么调也不会有正确阴影。

4. Shadow acne:表面为什么自己挡自己

Shadow Map 是离散深度样本,第二遍重新计算的深度与第一遍不可能逐位相同。倾斜表面上的相邻片元可能落到同一阴影纹素,微小误差让表面误判为被自己遮挡,出现条纹状 acne。

最简单的补救是加入 bias。固定 bias 太小压不住 acne,太大会让阴影与物体分离,形成 Peter Panning。常用做法让 bias 随法线与光线夹角增加:

bias=max⁡(bmin,k(1−n⋅l)).\text{bias} =\max\left( b_{min}, k(1-\mathbf n\cdot\mathbf l) \right).

5. 阴影边缘为何锯齿

Shadow Map 的分辨率有限,而且光源视锥里每个纹素映到相机画面的面积并不均匀。光源视野过宽、场景范围过大或观察距离变化,都可能让阴影欠采样。

Percentage Closer Filtering 不直接插值深度,而是在附近多个纹素上分别做“亮/暗”比较,再平均比较结果:

S(P)=1N∑i=1N1[zP≤zi+bias].S(P)=\frac1N\sum_{i=1}^N \mathbf 1[z_P\le z_i+\text{bias}].

这会软化锯齿边缘,但它主要是一种过滤近似,不等于真实面积光源半影。

6. 优缺点与使用边界

Shadow Mapping 的优点是:

  • 与场景几何复杂度耦合较弱。
  • 能直接复用光栅化和深度缓冲硬件。
  • 不要求显式求物体间交线。

缺点是:

  • 质量受 Shadow Map 分辨率和投影分配影响。
  • 有自遮挡误差和视锥覆盖问题。
  • 透明物体、软阴影和多光源需要额外处理。

课件最后的 projective texturing 与它使用同一条坐标链:把点投到另一个“投影设备”的纹理坐标中。区别是前者取颜色,Shadow Mapping 取深度并做可见性比较。

评论