【C++游戏引擎开发】第24篇:级联阴影映射(CSM,Cascaded Shadow Maps)
一、阴影映射技术基础
1.1 阴影映射的核心原理
1.1.1 深度测试与阴影生成
阴影映射(Shadow Mapping)是一种基于深度比较的阴影渲染技术,其核心思想分为两步:
- 从光源视角渲染深度贴图:将场景渲染到光源的深度缓冲区,生成阴影贴图(Shadow Map)。
- 从摄像机视角进行深度比较:在正常渲染时,将像素点转换到光源空间,比较其深度值与阴影贴图记录的深度值,决定是否处于阴影中。
数学上,阴影可见性计算可表示为:
V ( p ) = { 0 若 d fragment > d shadow map 1 否则 V(p) = \begin{cases} 0 & \text{若 } d_{\text{fragment}} > d_{\text{shadow map}} \\ 1 & \text{否则} \end{cases} V(p)={01若 dfragment>dshadow map否则
其中 d fragment d_{\text{fragment}} dfragment 是当前片元到光源的深度, d shadow map d_{\text{shadow map}} dshadow map 是阴影贴图中存储的最近深度。
1.1.2 关键问题:透视走样(Perspective Aliasing)
传统阴影映射在以下场景中表现不佳:
- 近处物体:阴影贴图分辨率不足导致锯齿(锯齿状阴影边缘)
- 远处物体:相同纹理像素覆盖过大区域,导致模糊(过度稀疏采样)
根本原因在于透视投影下深度分布的非线性,使得固定分辨率的阴影贴图无法均匀分配精度。
1.2 阴影映射的数学基础
1.2.1 光源空间变换
- 模型-光源视图矩阵(Light View Matrix):将物体从世界坐标系变换到光源视角的视图空间。
- 光源投影矩阵(Light Projection Matrix):通常使用正交投影(方向光)或透视投影(点光源)。
组合后的变换矩阵为:
M light = M projection × M view M_{\text{light}} = M_{\text{projection}} \times M_{\text{view}} Mlight=Mprojection×Mview
1.2.2 深度值非线性分布
在透视投影中,深度值 z z z 的归一化计算公式为:
z ndc = f + n f − n + 2 f n f − n ⋅ 1 z z_{\text{ndc}} = \frac{f+n}{f-n} + \frac{2fn}{f-n} \cdot \frac{1}{z} zndc=f−nf+n+f−n2fn⋅z1
其中 n n n 和 f f f 为近、远平面,导致深度缓冲区中近处精度高、远处精度低。
1.3 阴影映射的局限性
1.3.1 自阴影问题(Shadow Acne)
由于深度贴图分辨率和浮点精度限制,表面可能错误地判定自身处于阴影中。常用解决方案:
- 深度偏移(Depth Bias):添加固定或斜率缩放偏移量:
d adjusted = d fragment + bias d_{\text{adjusted}} = d_{\text{fragment}} + \text{bias} dadjusted=dfragment+bias
1.3.2 Peter-Panning 现象
过度使用深度偏移会导致物体与阴影分离(“漂浮”效果)。需在视觉质量与偏移量之间权衡。
二、级联阴影映射(CSM)理论
2.1 CSM 的设计动机
2.1.1 动态分辨率分配
将摄像机视锥体(View Frustum)按深度分割为多个子区域(级联),每个级联独立生成阴影贴图:
- 近层级:高分辨率贴图,捕捉细节
- 远层级:低分辨率贴图,节省资源
2.1.2 视锥体分割策略
常用的分割方式包括:
- 均匀分割(Uniform Split):线性划分深度范围
z i = n + i k ( f − n ) z_i = n + \frac{i}{k}(f-n) zi=n+ki(f−n) - 对数分割(Logarithmic Split):适应透视分布
z i = n ( f n ) i / k z_i = n \left( \frac{f}{n} \right)^{i/k} zi=n(nf)i/k - 实用分割(Practical Split):混合线性与对数分割
2.2 CSM 的算法流程
2.2.1 视锥体分割与层级计算
- 将摄像机视锥体分割为 k k k 个层级(通常 k = 4 k=4 k=4)
- 对每个层级计算其包围盒(AABB)
2.2.2 光源投影矩阵优化
为每个层级生成紧贴包围盒的正交投影矩阵,最大化利用阴影贴图像素:
- 将层级包围盒变换到光源空间
- 计算包围盒在光源空间的最小/最大坐标
- 构建正交投影矩阵:
M ortho = [ 2 r − l 0 0 − r + l r − l 0 2 t − b 0 − t + b t − b 0 0 2 f − n − f + n f − n 0 0 0 1 ] M_{\text{ortho}} = \begin{bmatrix} \frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\ 0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\ 0 & 0 & \frac{2}{f-n} & -\frac{f+n}{f-n} \\ 0 & 0 & 0 & 1 \end{bmatrix} Mortho= r−l20000t−b