(论文)[2019-RTGEM] Ray Traced Shadows: Maintaining Real-Time Frame Rates
Ray Traced Shadows
- 适应性采样:通过局部变化量来计算采样样本数
- 极差 => 空间滤波 => 时间平均
- 后处理
- 时域平均 + 空间(可变半径高斯双边滤波)【半径由局部变化量决定】
Introduction
- 光栅化阴影的问题
- Perspective aliasing【锯齿】
- 自遮挡问题【shadow acne,peter panning】
- 软阴影
- 半透明物体
- 这个论文对 DI 的近似
- 【式子没有考虑自发光,每个光源单独计算】
- 这里阴影的定义就是 area-domain 的平均
- 着色使用光源中心计算
\[ L_d(P,\omega_o) \approx f(P,\omega_C,\omega_o) L_a(C,\omega_C) \frac{ (\omega_C\cdot\hat{\mathbf{n}}_P) (-\omega_C\cdot\hat{\mathbf{n}}_C) }{ \left\Vert P-C\right\Vert^2 } \frac{1}{|S|} \sum_{X\in S} v(P,X) \]
Adaptive Sampling
Temporal Reprojection
- 两个目的
- 估计 visibility variation【可见性变化程度】,从而计算光线数
- 决定后处理的 kernel size
- 一共保存 4 帧的样本作为 cache
- 将当前帧重投影回前 3 帧【reverse reprojection】
- 【clip space,投影矩阵变换后未归一化的空间】
- 假设世界空间位置不变
\[ \bar{P}_{t-1} = C_{t-1} V_{t-1} V_t^{-1} C_t^{-1} P_t \]
- 通过深度连续性判断是否保留
- \(c_1=0.003,c_2=0.017\)
- \(\hat{\mathbf{n}}_z\):view space normal
- \(P^{z}\):深度【只保留了上一帧就足够】
- 我看实现代码,就是把 \(\epsilon=0.003\)
\[ \left| 1-\frac{\bar{P}_{t-1}^{z}}{P_{t-1}^{z}} \right| < \varepsilon, \quad \varepsilon = c_1+c_2\left|\hat{\mathbf{n}}_z\right| \]
半影区域判定
- 启发式的使用 temporal variation measure
- 四帧历史的 visibility 极差【存在一个 float4 中】
- 历史 visibility 是重投影后的
\[ \Delta v_t(x) = \max\left( v_{t-1}(x),\ldots,v_{t-4}(x) \right) - \min\left( v_{t-1}(x),\ldots,v_{t-4}(x) \right) \]
- 维持时域稳定,对 \(\Delta v_t\)
进行 spatial filter 得到 \(\widetilde{\Delta
v_t}\)
- 5x5 maximum filter【2 pass】
- 13x13 tent filter【倒三角权重形式】
- 实现代码:9x9 average filter【2 pass】
- 和历史保存信息平均
\[ \overline{\Delta v}_t = \frac{1}{2} \left( \widetilde{\Delta v}_t + \frac{1}{4} \left( \Delta v_{t-1} + \Delta v_{t-2} + \Delta v_{t-3} + \Delta v_{t-4} \right) \right) \]
分配样本
- 通过阈值 \(\delta\) 动态调整样本个数【实现用的 0.1】
- \(s_{\max}\):标准 5,高质量模式 8
- 调整
- \(\overline{\Delta v}_t>\delta\):\(s_{t}(x)=\min\left(s_{t-1}(x)+1,s_{\max}\right)\)
- \(\overline{\Delta v}_t<\delta\):\(s_{t}(x)=\max\left(s_{t-1}(x)-1,0\right)\)
- 比较稳定
- 如果重投影失效,那么就重置为当前帧结果;设置为 \(s_{\max}\)
Sampling Mask
- variation 为 0 表示全白/全黑;直接使用历史信息
- 相机慢速 zoom、光源快速移动可能出问题【重投影成功,但是 visibility 变化】
- 实现:4 个像素至少发一条光线
- 直接这么做性能低,warp 依赖的问题【和都发一条性能差不多】
- 采样一个 block \(n_b\times n_b\)
【8x8 效果不错】
- 只有被选中的 block 内部,variation = 0,此时才不发光线
- 实现上使用一个 4x4 的 block mask【循环】,用于标记当前 block 是否被选中【block 对应的 mask = frameCount%4 则被选中】

计算最终值
- temporal filtering + spatial filtering
- 相关工作:SVGF,Optix
- 这里使用简单的实现方案
- 时域
\[ \tilde{v}_t = \frac{1}{4} \left( v_t + v_{t-1} + v_{t-2} + v_{t-3} \right) \]
- 空域:可变半径双边滤波【1x1 => 9x9】【2 pass】
- 半径和 \(\widetilde{\Delta v}_t\)
线性相关【kernel 半径:\(\widetilde{\Delta
v}_t/\eta\times4\)】
- 把最大半径对应到 \(\widetilde{\Delta v}_t=\eta=0.4\)
- 防止抖动:预定义高斯权重,然后从相邻的两级高斯进行线性插值【根据半径】
- 判断当前像素能否复用
- view-space normal 差距【点积 > 0.9】
- 深度差异:通过视线方向插值【掠射角取最大阈值,正射最小阈值】【判断是不是属于一个表面】
- 阈值:0.003;0.02
- 半径和 \(\widetilde{\Delta v}_t\)
线性相关【kernel 半径:\(\widetilde{\Delta
v}_t/\eta\times4\)】
1 | bool isValidTap(float tapDepth, float centerDepth, float3 tapNormal, float3 centerNormal) |
- 这样的空域滤波变成不可分的,但是还是 2 个 pass 实现,效果还行
实现
- 对 Spherical Light 内部采样进行了优化【蓝噪声类似,球内分布更加均匀】
- 光源剔除:到光源距离
- 总样本数控制:通过计算 \(\overline{\Delta
v}_t\) 的和【自定义层级 mipmap 计算】
- 对最后得到的样本数进行 scale
- 整体 pipeline
