ReSTIR DI - 01 - 无复用版本
因为最近在给Pathtracer加一点Optix backend,想说既然加了,那就加点新东西(好吧其实也是5-6年的老东西了),就选择了ReSTIR DI作为学习和实现的目标。
长话短说,ReSTIR DI我个人认为是改进NEE直接光源采样的过程中,光源样本可能不佳的问题。解决方式是通过额外的信息(不管是预先计算每个sample的预期贡献,还是样本复用也好),来尽量让我们采样到更好的光源sample。
1. Direct lighting 本来要求什么?
对一个 shading point $x$,direct lighting 可以写成:
$$
L_o(x) = \int_{\mathcal{Y}} f(x, y),dy
$$
这里 $y$ 可以理解成一个 light sample,例如:
$$
y = (\text{light id},\ \text{point on light})
$$
而 $f(x,y)$ 包含:
$$
\begin{aligned}
f(x,y) &=
L_e(y)
\cdot f_r(x,\omega_i,\omega_o)
\cdot G(x,y)
\cdot V(x,y)
\end{aligned}
$$
也就是 emission、BRDF、geometry term、visibility。
普通 NEE 如果从某个 proposal distribution $p(y)$ 采样,就估计:
$$
\hat{L} =
\frac{f(x,y)}{p(y)}
$$
问题是:如果场景里有很多灯,均匀选 light 会非常 noisy。因为大部分 sampled light 对当前 shading point 都贡献很小,甚至被遮挡。
2. ReSTIR DI 的核心:不要直接相信第一次采到的 light
ReSTIR 的想法不是“采一个 light 就用它”,而是:
先以较低成本采样很多 candidate light,然后从里面挑一个更像“重要样本”的 candidate 保存下来。
这个过程就是 Resampled Importance Sampling / RIS。
假设你从 source distribution $p_{\text{src}}(y)$ 采了 $M$ 个 candidates:
$$
y_1, y_2, …, y_M
$$
然后给每个 candidate 算一个 importance weight:
$$
w_i = \frac{\hat{p}(y_i)}{p_{\text{src}}(y_i)}
$$
这里 $\hat{p}(y)$ 是希望靠近的 target importance,通常和贡献大小相关。对 ReSTIR DI 来说,常见选择是:
$$
\hat{p}(y) \approx |f(x,y)|
$$
实际实现里可能用 luminance:
$$
\hat{p}(y) = \operatorname{luminance}(f_{\text{unshadowed}}(x,y))
$$
也就是说,越亮、BRDF 越对、geometry term 越大,candidate 越容易被 reservoir 选中。
这里的$p_{src}$就是你一般情况下从light list里获取一个light sample的概率。
这个概率分为1.选中指定light source的概率(看你选用的heuristic,论文里每个光源的权重应该是基于power)2.在指定光源上采样一个light sample的概率,一般是uniform sampling per surface area.
这里的 unshadowed contribution 通常包含:
$$
\begin{aligned}
f_{\text{unshadowed}}(x,y) &=
L_e(y)
\cdot
f_r(x,\omega_i,\omega_o)
\cdot
G(x,y)
\end{aligned}
$$
其中:
$$
G(x,y) =
\frac{|\cos\theta_x| |\cos\theta_y|}{|x-y|^2}
$$
如果光源是 directional light 或 environment light,geometry term 的形式会不一样;但核心思想一样:用一个便宜(不需要trace shadow ray)的估计判断这个 sample 值不值得保留。
从采样来说:
从采样角度看,ReSTIR DI 的核心不是直接构建一个理想采样分布,而是做一次基于 candidates 的近似重采样。理想情况下,我们希望从
$$
p^*(y|x) \propto |f(x,y)|
$$
中采样,但这个分布依赖当前 shading point、BRDF、geometry term 和 visibility,难以直接构建、归一化和采样。相比之下,$p_{\text{src}}(y)$ 例如基于 light power 或面积的 light sampling 分布虽然粗糙,但容易采样。
因此 ReSTIR DI 先从 $p_{\text{src}}$ 生成有限个 candidates,再对这些 candidates 计算更接近真实贡献的 target importance $\hat p(x,y)$,并用
$$
w_i = \frac{\hat p(x,y_i)}{p_{\text{src}}(y_i)}
$$
进行重采样。这样做的直觉是:我们不需要能直接从 $\hat p$ 采样,只需要能对已经生成的 candidates 评估 $\hat p$,就可以让最终被保留的 sample distribution 更接近理想贡献分布。
这里并不是换了一个采样空间;sample space 仍然是所有可能的 light samples $\mathcal{Y}$,变化的是我们在这个空间上的采样分布。
3. Initial reservoir:从多个 candidates 中压缩出一个 light sample
现在对当前 shading point $x$,我们不只采一个 light sample,而是采 $M$ 个 candidates:
$$
y_1, y_2, …, y_M
$$
每个 candidate 都来自同一个或者不同的 source distribution:
$$
p_{\text{src}}(y_i)
$$
对每个 candidate,我们计算一个 importance weight:
$$
w_i = \frac{\hat{p}(x,y_i)}{p_{\text{src}}(y_i)}
$$
这里把 $\hat{p}$ 写成 $\hat{p}(x,y_i)$,是为了强调:target importance 是相对于当前 shading point 的。
常见做法是:
$$
\hat{p}(x,y_i) =
\operatorname{luminance}
\left(
f_{\text{unshadowed}}(x,y_i)
\right)
$$
也就是先不考虑 visibility,只看如果这个 light sample 没被挡住,它对当前点大概有多重要。
这里的 unshadowed contribution 通常包含:
$$
\begin{aligned}
f_{\text{unshadowed}}(x,y) &=
L_e(y)
\cdot
f_r(x,\omega_i,\omega_o)
\cdot
G(x,y)
\end{aligned}
$$
其中:
$$
G(x,y) =
\frac{|\cos\theta_x| |\cos\theta_y|}{|x-y|^2}
$$
如果光源是 directional light 或 environment light,geometry term 的形式会不一样;但核心思想一样:用一个便宜的估计判断这个 sample 值不值得保留。
3.1 Reservoir update 的流程
reservoir 初始化:
1 | Reservoir r; |
对每个 candidate:
1 | LightSample y = sampleLightSource(); // 采样光源 |
最后计算 reservoir weight:
$$
W = \frac{w_{\text{sum}}}{M \cdot \hat{p}(x,y)}
$$
也就是:
1 | if (r.target > 0 && r.M > 0) { |
最终 shading 时:
1 | Vec3 f = evalFullContributionWithVisibility(x, r.y); |
如果最后选中的 sample 不可见:
1 | Lo = Vec3(0); |
3.2 为什么要算 $w_i = \hat{p} / p_{\text{src}}$?
候选样本是从 $p_{\text{src}}$ 生成的,但 reservoir 希望最终保留的样本分布更接近 target importance $\hat p$。如果只用 $\hat p$ 作为权重,就会忽略 candidate 本身出现的概率;那些本来就容易被 proposal 采到的样本会被重复偏袒。因此 RIS 使用
$$
w_i=\frac{\hat p(x,y_i)}{p_{\text{src}}(y_i)}
$$
来补偿 source distribution 的影响。
直觉上,一个 sample 先以 $p_{\text{src}}(y)$ 的概率出现在 candidate pool 中,然后 reservoir 再用 $\hat p(x,y)/p_{\text{src}}(y)$ 作为保留倾向。两者相乘后:
$$
\begin{aligned}
p_{\text{src}}(y)
\cdot
\frac{\hat p(x,y)}{p_{\text{src}}(y)}
&=
\hat p(x,y)
\end{aligned}
$$
因此最终被保留的趋势近似正比于 target importance。
例如,一个小但很亮、离当前 shading point 很近的 area light,可能因为面积小或 light selection probability 低而很难被 $p_{\text{src}}$ 采到;但一旦它出现在 candidate pool 中,它的 $\hat p(x,y)$ 很高,所以 $\hat p/p_{\text{src}}$ 会让它有更高概率被 reservoir 保留。
3.3 为什么最后要除以 $\hat{p}(x,y)$?
因为 reservoir 选中的样本已经经过了一次按 target importance 倾斜的重采样。
如果最后直接返回 $f(x,y)$,就相当于重复奖励了高 $\hat p$ 的 sample,结果通常会偏亮。
所以需要做校正:
$$
W = \frac{w_{\text{sum}}}{M \cdot \hat{p}(x,y)}
$$
最终:
$$
\hat{L} =
f(x,y) W
$$
代入:
$$
\begin{aligned}
W &=
\frac{1}{\hat{p}(x,y)}
\cdot
\frac{1}{M}
\sum_{i=1}^{M}
\frac{\hat{p}(x,y_i)}{p_{\text{src}}(y_i)}
\end{aligned}
$$
直觉是:
Reservoir 用 $\hat{p}$ 让好 sample 更容易被选中; $W$ 再把这种偏向性抵消掉,让 estimator 尽量保持正确。
4. 为什么reservoir能降 variance?
因为做了一个 local light selection。
假设有 16 盏灯,均匀 NEE 每次随机选一盏。某个 shading point 可能主要被其中 1–2 盏灯照亮。普通 NEE 选中重要灯的概率很低。
但 no reuse ReSTIR / RIS 做的是:
- 一次拿 $N$ 个 candidates;
- 对每个估计 importance;
- 更大概率保留重要 candidate;
- 用 $W$ 修正选择偏差。
所以 $N=4$ 时,你等于每个 shading point 看了 4 次 light proposal,再选一个更好的。这自然会比 $N=1$ 稳定很多。
无复用情况下的预期表现:
- $M=1$ 退化到 NEE;
M=1时:
$$
w_{\text{sum}} = \frac{\hat p(x,y)}{p_{\text{src}}(y)}
$$
所以:
$$
W =
\frac{w_{\text{sum}}}{M\hat p(x,y)} =
\frac{\hat p(x,y)/p_{\text{src}}(y)}
{\hat p(x,y)} =
\frac{1}{p_{\text{src}}(y)}
$$
最终退化到普通的NEE
$$
\hat L = f(x,y)W = \frac{f(x,y)}{p_{\text{src}}(y)}
$$
- 多光源场景下,随着initial sample上升,variance 明显下降;