三 实时阴影 Real-Time Shadows¶
本课程为闫令琪老师的 GAMES202-高质量实时渲染
此为个人的课堂笔记,如有疏漏,请多指正
1 阴影映射概述 Shadow Mapping¶
1)使用两趟 Pass 实现阴影¶
- 第一个 Pass 获得从 Light 看向场景时(将摄像机放到光源位置上)的深度信息,输出为阴影纹理 Shadow Map
- 第二个 Pass 从真正的摄像机的位置渲染整个场景,并使用阴影映射采样计算每个点是否会处在阴影里
具体内容参考 GAMES101-几何(网格处理)、阴影图
2)阴影映射技术的优缺点¶
- 优点:输出了阴影纹理之后,就不需要场景的几何信息了,仅需对阴影纹理采样即可
- 缺点:会出现自遮挡现象和锯齿,并且只能实现硬阴影
(1)自遮挡现象 Self Occlusion¶
由于是从阴影映射纹理进行采样,某个像素的深度信息可能会被邻近像素的信息覆盖掉(因为像素是有大小的)。当光线与表面越平行,问题越严重

解决方案1:添加一个可变的 bias 来减少自遮挡现象。但是会导致某些原本虽然距离很近但应该显示的阴影发生丢失现象

解决方案2:Second-depth shadow mapping 技术,计算最小深度和次小深度两个深度信息,使用二者的平均值作为深度信息来做后续的阴影计算。但是这种方法要求投射阴影的物体必须为闭合曲面 (watertight) ,并且多了一个 Pass 导致性能消耗变大,和实时渲染的速度要求不匹配,即使它的复杂度和只计算一次是相同的——实时渲染不相信算法复杂度,只相信实际的绝对速度

(2)锯齿现象¶
由于阴影纹理本身是有一定大小的,如果分辨率不够大,则某些阴影处会出现锯齿现象(也是因为像素是有大小的)
2 阴影映射技术背后的数学¶
由于实时渲染不要求“绝对精确”,而是要求“看起来是正确的”,因此有一个约等式便十分关键:
在两种情况下这个约等式的近似度更高:
- \(g(x)\) 的 support 特别小的时候,即积分率(支撑集)特别小的时候
- \(g(x)\) 特别平滑 smooth 的时候,即在积分率范围内变动特别小,最大值和最小值差别很小(甚至相同,即常量函数)的时候
在渲染方程中,可以将 可见度\(V\) 作为 \(f(x)\) 提出来,则 \(g(x)\) 就是最普通的 shading,这样就可以在计算完 shading 之后再乘上 可见度\(V\),这也便是阴影映射技术的实现思想(先计算 shading,然后再将阴影映射纹理采样的结果乘到这个 shading 上),也是其效果比较好的原因
而对应于两种近似度更高的情况:
- \(g(x)\) 的 support 特别小的时候,对应着只有一个点光源 point lighting 或方向光源 directional lighting 的情况(要么可见要么不可见)
- \(g(x)\) 特别平滑 smooth 的时候,对应着光源是一个不会有光强变化的面光源 constant radiance area lighting 或表面只有漫反射 diffuse BRDF 的情况
3 百分比渐进软阴影 Percentage Closer Soft Shadows (PCSS)¶
软阴影 Soft Shadows:没有一个从无阴影到有阴影的区分线,而是渐进变化的。因为日常生活中的光源往往是面光源,其产生的阴影就是软阴影(区别于点光源和方向光源的硬阴影),阴影的部分往往总是部分被遮挡部分没有被遮挡
1)百分比渐进滤波 Percentage Closer Filtering (PCF)¶
PCF 是一种解决 Shadow Map 存在的锯齿问题的滤波算法。具体做法是用 采样点x 的深度去和周围区域点在 Shadow Map 上的深度做比较,将比较的结果加权平均后作为 采样点x 的可见性,于是阴影的数值从非黑即白的0和1变为连续的0至1之间的颜色
注意:PCF 不是对最后生成的图像进行滤波,也不是在 Shadow Map 上直接进行滤波,而是对采样的结果进行滤波
2)百分比渐进软阴影 Percentage Closer Soft Shadows (PCSS)¶
在 PCF 的基础上,如果 Filter 取的过大,则某个点的阴影会和过多的点发生加权,导致结果上虽然实现了抗锯齿,但是阴影效果就完全糊掉了。而这种“糊掉”虽然对硬阴影是不正确的,但却正是软阴影所需要的。因此,可以通过调整 Filter 的取值范围来实现不同需求的软阴影,即 PCSS 技术
使用 PCSS 实现软阴影最关键的问题在于如何取 Filter 的值。滤波半径决定了阴影边缘的柔和程度,一般来说滤波半径越大则柔和,相反阴影边缘越锐利。通过观察实际现象可发现,阴影区域在越靠近遮挡物的地方越硬,而越远离遮挡物的地方越软,因此可以根据投影区域到遮挡物的距离来调整滤波半径的大小

由于现实中的光源往往是面光源而不是完全理想的点光源或方向光源,其光线被遮挡后会产生半影,阴影的软化程度本质上取决于半影区域的大小,半影区域越大则阴影越柔和。当面光源位置不变时,遮挡物 Blocker 与接收阴影的面的距离越远,则半影区域的面积会越大,阴影越软;而当遮挡物 Blocker 与接收阴影的面的距离不变时,面光源的宽度越宽,则半影区域的面积会越大,阴影越软

根据三角形相似得到如下等式:
其中,\(d_{Receiver}\) 是阴影接收区域到光源的距离,\(d_{Blocker}\) 是遮挡物到光源的距离,这两个参数都可以直接从第一个 Pass 生成的深度图获取得到,光源面积 \(w_{light}\) 手动指定。上述等式计算得到的半影长度 \(W_{Penumbra}\) 就可以作为 PCF 滤波半径的大小参数
因此,PCSS 实现软阴影的基本流程如下:
- 查找遮挡物的平均深度 Blocker Search:获得一个特定区域内的遮挡物的平均遮挡距离 getting the average blocker depth in a certain region (注意:是发生遮挡的物体的平均深度,在这个 certain region 里不发生遮挡的点不参与计算)
- 伴影估计 Penumbra estimation:使用平均遮挡物距离决定滤波核大小 use the average blocker depth to determine filter size
- 百分比渐进滤波 Percentage Closer Filtering:根据滤波核大小对该区域内的深度值进行加权平均
在这里,第1步中查找遮挡物的平均深度时需要一个特定范围 a certain region,这个特定范围可以经验性取值,或者根据如下示意图,根据点连接到面光源的距离 receiver's distance from the light + 面光源大小 light size 共同决定 a certain region(图中红色的部分):

3)PCSS 存在的问题¶
以上 PCSS 流程的耗时特别长,主要在于第1步和第3步都需要进行一个区域内的采样:一旦这个 Filter 的区域过大(为了保证软阴影的实现效果),耗时就会过长。工业界的解决方案之一是在这个区域内随机地进行稀疏采样以达到近似的效果,例如随机圆盘采样和泊松圆盘采样,但是有可能会引入噪声的问题
4 方差软阴影映射 Variance Soft Shadow Mapping (VSSM)¶
VSSM 是针对于 PCSS 在第1步和第3步都需要对深度图进行采样而导致耗时特别长的问题而提出的一种解决方案
1)VSSM 的基本思想¶
PCSS 的第3步,本质上是通过卷积算法算区域深度的平均值,然后跟当前深度进行比较,根据这个比较值进行阴影的淡化。这个过程完全可以理解为找到当前深度在该区域内的百分比位置,直接用这个百分比来淡化阴影
而 PCSS 的第1步中有关于遮挡物深度平均值的计算过程,也可以利用这个百分比的思想快速求解(具体的快速求解过程参考下一小节)
于是,VSSM 便基于这个百分比的思想进行了改进:不再遍历 a certain region 中的所有点,而是利用它们的深度值的正态分布快速得到当前点的百分比位置。而为了得到深度值的正态分布,仅需知道所有深度值的平均值和方差即可,其中平均值可以通过 Summed Area Tables (SAT) 算法快速获得,方差可以通过 \(Var(X)=E(X^2)-E^2(X)\) 快速获得。进一步,为了避免通过查误差函数 erf 的表来获得正态分布的累积分布函数 CDF,可以利用切比雪夫不等式近似获得当前正态分布的互补累积分布函数值,即右尾函数 \(Q(x)\),将查表的复杂度降至常数级
上述这个掺杂了各种近似计算的过程可以用如下流程表示:
- 根据 SAT 算法快速求解平均值
- 根据平均值快速求解方差
- 根据平均值+方差+切比雪夫不等式快速获得正态分布的互补累积分布函数,即右尾函数 \(Q(x)\)
- 根据右尾函数 \(Q(x)\) 快速获得当前点的百分比位置
(1)Shadow Map 深度值平均值的求解¶
Summed Area Tables (SAT) 算法:算法思想为前缀和 prefix sum,利用一张额外的表记录前缀和,通过空间换时间
一维情况:

\(Sum(index3 \rightarrow index5) = 3 + 7 + 1 = 11 = SAT(5) - SAT(2) = 20 - 9 = 11\),其中 \(SAT(i)=\sum\limits_{j≤1}Texture(j)\)
二维情况:

\(S(ABCD) = SAT(D) - SAT(B) - SAT(C) + SAT(A)\),其中 \(SAT(x,y)=\sum\limits_{x'≤x,y'≤y}Texture(x',y')\)
在 Shadow Map 中获得特定区域的深度平均值的计算过程就和二维情况相同
(2)Shadow Map 深度值方差的求解¶
对于正态分布来说,\(Var(X)=E(X^2)-E^2(X)\),因此只需要在 Shadow Map 深度纹理的另一个 rgb 某个通道中同时记录深度值的平方即可
(3)切比雪夫不等式近似获得互补累积分布函数值¶

当 t 值取在正态分布的右半边时满足切比雪夫不等式,因此就可以通过平均值和方差直接计算得到互补累积函数,即右尾函数 \(Q(x)\),从而直接得到 t 值对应的百分比位置
通过以上思想,可以将每一个步骤的时间复杂度控制在常数级,完全不会有任何循环遍历的操作
2)VSSM 实现软阴影的基本流程¶
同样是 PCSS 的三步流程,但是第1步和第3步的相关循环遍历操作转换为快速的近似计算:
(1)查找遮挡物的平均深度 Blocker Search¶
获得一个特定区域内的遮挡物的平均遮挡距离 getting the average blocker depth in a certain region
VSSM 中不进行该区域的循环遍历,而是通过下面这个等式直接计算得到遮挡物的平均深度

其中:
- 特定区域 certain region 的平均深度 \(Z_{Avg}\):通过 VSSM 的基本思想——Summed Area Tables (SAT) 算法直接从整张纹理取得这一块特定区域的平均深度
- 非遮挡物的比例 \(N_1/N\) 和遮挡物的比例 \(N_2/N\):通过 VSSM 的基本思想——切比雪夫不等式近似获得 \(N_1/N = P(x > t)\) 和 \(N_2/N = 1 - P(x > t)\)
- 非遮挡物的平均深度 \(Z_{unocc}\):直接假定是当前查找的这个点的深度,即 \(Z_{unocc} = t\)
然后就可以将上述等式视为一个一元一次方程解得遮挡物的平均深度 \(Z_{occ}\) 的值
(2)半影估计 Penumbra estimation¶
使用平均遮挡物距离决定滤波核大小 use the average blocker depth to determine filter size
使用上述等式计算得到的半影长度 \(W_{Penumbra}\) 作为 PCF 滤波半径的大小参数
(3)百分比渐进滤波 Percentage Closer Filtering¶
根据滤波核大小对该区域内的深度值进行加权平均
VSSM 中不进行该区域的循环遍历,不进行加权平均的计算,而是直接通过 VSSM 的基本思想——切比雪夫不等式近似获得该区域比当前深度值 t 小的点占总数的比例 \(ratio\),则当前点的可见性即为 \(V \cdot ratio\)
例如当前点深度为3.5,Shadow Map 上该查找区域的深度值分别为 {1,2,3,4,5,6,7,8,9},则希望利用切比雪夫不等式近似得到值3 / 9 = 0.333
3)VSSM 存在的问题¶
由于使用了切比雪夫不等式,必须要求区域内的深度值是呈正态分布的。一旦分布不是正态的(例如离散多峰的情况),就会导致利用切比雪夫不等式计算的 \(Q(x)\) 大小不正确,导致阴影强度偏强(更暗)或变弱(更亮,即漏光现象 Light Leaking)


5 矩阴影映射 Moment Shadow Mapping¶
Moment Shadow Mapping 的基本数学原理是:当记录更多阶的矩 (moment),就可以拟合出更准确的函数曲线
具体到 VSSM 中,计算方差时使用了一阶矩 \(X\) 和二阶矩 \(X^2\),因此 VSSM 记录了两个矩来拟合 CDF 曲线
每记录前 \(m\) 阶矩,就可以更准确地拟合有 \(m/2\) 个 step 的阶跃函数:

使用 Moment Shadow Mapping 便可以避免 VSSM 中可能发生的漏光现象:

但是由于四阶函数的拟合计算更加复杂,这也会导致时间成本的显著增加
6 距离场软阴影 Distance Field Soft Shadow (DFSS)¶
距离场 Distance functions/Distance field: At any point, giving the minimum distance (could be signed distance -- signed distance field (SDF)) to the closest location on an object. 对于空间中的任意一点,它都有一个和某个物体表面的最近的一个点的距离(这些距离信息统称为距离场),并且这个距离可以根据在物体内部或者外部附上方向(即有向距离场)
一个二维距离场的可视化:

一个三维距离场的可视化:

1)有向距离场的应用1:光线步进 Ray Marching¶
Ray Marching 是一种用于实现光线追踪的技术
可以利用 SDF 进行 Sphere Tracing 来实现光线步进:SDF 定义了该点到最近表面的距离,这个距离是一个“安全距离”,表明了从该点出发向任意方向行进这么长的距离都不会与任何物体相交。因此,如果已经获得了场景的 SDF 信息,就可以很快地实现光线步进,直到光线和某个物体相交(SDF 对应值很小)或者超出光线最大距离

2)有向距离场的应用2:距离场软阴影 DFSS¶
可以利用 SDF 获得任意一个点的“安全角度”,即在这个角度范围内光线是不会被遮挡的。因此如果从某个着色点出发看向光源的“安全角度”越小,说明这个点被遮挡的程度越高,可见度越低,需要产生的阴影越黑(当该着色点看向面光源的所有点的安全角度均为0时,说明该点被完全遮蔽,可见度为0)。这种实现阴影的方法并不物理准确,但是效果比较好
注:对于任意一条光线,在 Trace 的过程中取得的多个“安全角度”中,取其中的最小值

而想要计算这个 角度\(\theta\),标准计算方法为取反三角函数:
但是反三角函数的计算量会很大,因此使用了如下近似计算方法:
其中,\(k\) 为缩放系数,其值可以决定阴影的软硬程度,\(k\) 值越大,则计算得到的半影大小会越小(因为 角度\(\theta\) 越大,则表示越不在遮挡范围内),阴影越硬:

3)SDF 存在的问题¶
SDF 的优势:速度快(只计算使用时的时间,而不计算预生成所需要的时间),高质量
SDF 的问题:需要时间较长的预计算,需要很大的存储空间(特别是非静态场景)
本篇笔记主要参考了以下两篇博客,感谢 WC Yang 和 zhiwei 两位大佬的分享:
《GAMES202:高质量实时渲染》1 实时阴影:阴影映射(Shadow Mapping)、PCSS、VSSM、SDF Shadows - 知乎 (zhihu.com)
2023年7月 ziao德