ReSTIR DI - 01 - 无复用版本

ReSTIR DI - 01 - 无复用版本

May 13, 2026

因为最近在给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
2
3
4
5
Reservoir r;
r.y = invalid; // 具体的light sample
r.wSum = 0; // candidate weight的和
r.M = 0; // 这个sample代表了多少个candidate
r.W = 0; // 最终的reservoir weight

对每个 candidate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LightSample y = sampleLightSource(); // 采样光源
float sourcePdf = computeSourcePdf(y);// 原本采样到这个light sample的pdf

Vec3 contrib = evalUnshadowedContribution(x, y);
float target = luminance(contrib); // // 预估贡献,作为 target importance function,不一定是归一化 pdf

float w = target / sourcePdf; // 分配的权重

// 以下为 reservoir sampling流程
r.wSum += w;
r.M += 1;

if (rand() < w / r.wSum) {
r.y = y;
r.target = target;
r.sourcePdf = sourcePdf;
}

最后计算 reservoir weight:
$$
W = \frac{w_{\text{sum}}}{M \cdot \hat{p}(x,y)}
$$
也就是:

1
2
3
4
5
6
if (r.target > 0 && r.M > 0) {
// 修正后的reservoir weight
r.W = r.wSum / (r.M * r.target);
} else {
r.W = 0;
}

最终 shading 时:

1
2
Vec3 f = evalFullContributionWithVisibility(x, r.y);
Vec3 Lo = f * r.W;

如果最后选中的 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 做的是:

  1. 一次拿 $N$ 个 candidates;
  2. 对每个估计 importance;
  3. 更大概率保留重要 candidate;
  4. 用 $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 明显下降;