Deferred visibility in RIS
7cbd9f9 · 2026-07-08 ↗
This article covers the estimator change in the second part of commit
7cbd9f9. Candidate scoring may omit shadow rays, but the retained candidate
must be evaluated with visibility before its contribution is returned.
Estimator and target
For a direct-light sample \(z\), write the contribution as
\[f(z)=V(z)\,g(z),\]where \(g\) is the unoccluded emission–BSDF–geometry contribution and \(V\in\{0,1\}\) is visibility. RIS may use the cheaper scalar target \(\hat p(z)\approx\lVert g(z)\rVert\), select \(Y\), and evaluate visibility once:
\[\widehat I = V(Y)g(Y)\frac{S}{M\hat p(Y)}.\]Assumptions: The proposal covers visible contribution, the unoccluded target is finite and positive wherever visible contribution is non-zero, the candidate weight sum is positive, and visibility is evaluated for the retained sample in the receiving context.
Conditioned on candidates \(X_{1:M}\), selection probability is \(w_i/S\), where \(w_i=\hat p(X_i)/q(X_i)\). Therefore
\[\begin{aligned} \mathbb E[\widehat I\mid X_{1:M}] &=\sum_i \frac{w_i}{S} V(X_i)g(X_i)\frac{S}{M\hat p(X_i)}\\ &=\frac1M\sum_i\frac{V(X_i)g(X_i)}{q(X_i)}. \end{aligned}\]Taking the outer expectation gives \(\int V(z)g(z)\,dz\). Visibility need not be part of the selection target, but it must remain in the final contribution.
If visibility is omitted from both scoring and final evaluation, the estimator integrates unoccluded lighting instead. If stored visibility is reused in a different shading context without validation, the same substitution can occur across frames.
Cost and variance
Let \(M\) be the candidate count and \(C_s\) the cost of one shadow ray. Testing every candidate costs approximately \(M C_s\). Testing only the winner costs approximately \(C_s\), plus winner re-evaluation. The visibility work saved is close to
\[(M-1)C_s,\]provided that candidate generation does not perform equivalent intersection work elsewhere.
This cost expression does not predict variance. In an open scene, \(\lVert g\rVert\) can be a useful predictor of \(\lVert Vg\rVert\). In a heavily occluded scene, RIS can repeatedly select a high-target candidate that becomes zero after the final shadow ray. The estimator remains unbiased under the stated assumptions, but its variance can increase.
Candidate scoring without a shadow ray
The exact skip branch in _evaluateLightSample is:
if (skipVisibility)
return MISContrib{.Emission = RGBToSpectrum(ls.Color, surface.lambda),
.ThroughputMul = RGBToSpectrum(bsdfValue * nDotL, surface.lambda),
.PNee = pNee,
.PBsdf = pBsdf,
.UseMis = useMis,
.VisibilityTested = false};
Emission, BSDF, cosine, and both proposal densities remain present.
VisibilityTested = false records the missing operation as data rather than
leaving it implicit in control flow. The target later uses the luminance of
this unoccluded spectral contribution.
The non-reservoir winner
After weighted selection, the ordinary RIS branch checks the flag:
SampledSpectrum winnerThroughput{chosenCandidate.Throughput};
// ...
if (!chosenCandidate.VisibilityTested && chosenCandidate.Candidate.has_value())
{
const LightCandidate &lc{*chosenCandidate.Candidate};
const MISContrib nee{_evaluateLightSample(isect, *lc.Light, lc.Ls, scene, /*skipVisibility=*/false)};
winnerThroughput = nee.Emission * nee.ThroughputMul;
}
return winnerThroughput * W;
Passing false forces the full light evaluation, including the shadow ray.
The returned winnerThroughput therefore contains \(V(Y)g(Y)\), and W
contains the RIS normalization derived above.
The reservoir winner
The persistent branch performs the same obligation before storing the current reservoir:
if (finalized.ChosenSample.has_value() && !finalized.ChosenSample->VisibilityTested &&
finalized.ChosenSample->Candidate.has_value())
{
RISLightCandidate &winner{*finalized.ChosenSample};
const LightCandidate &lc{*winner.Candidate};
const MISContrib nee{_evaluateLightSample(isect, *lc.Light, lc.Ls, scene,
/*skipVisibility=*/false)};
winner.Throughput = nee.Emission * nee.ThroughputMul;
winner.VisibilityTested = true;
}
The stored sample is marked as tested for the context in which it was finalized. That flag alone does not make the visibility valid in a later receiving context. At this revision, temporal merging still accepts stored visibility more broadly than the final GPU implementation. The history limitations described in step 2 therefore remain relevant.
For a finite light, re-evaluation reconnects the retained light sample to the current surface and traces toward its endpoint. For an environment, the retained direction defines the visibility ray. The contribution, PDF, target, and visibility test must refer to compatible geometric interpretations.
Caption to keep: Deferred visibility reduces candidate shadow rays; its variance depends on how accurately the unoccluded target predicts visible contribution.