Fix #131723: Cycles volume not sampling channels with zero extinction

The original paper uses the single scattering albedo `sigma_s/sigma_t`
to pick a channel for sampling the scattering distance. However, this
only considers the situation where there is scattering inside the volume.
If some channel has an extinction coefficient of zero, the light passes
through without attenuation for that channel. We assign such channel
with a weight of 1 instead of 0 to make sure it can be sampled.

Pull Request: https://projects.blender.org/blender/blender/pulls/131741
This commit is contained in:
Weizhen Huang
2024-12-13 10:27:53 +01:00
committed by Weizhen Huang
parent b8d67aa10f
commit 27fc091be8
2 changed files with 9 additions and 5 deletions

View File

@@ -486,9 +486,12 @@ ccl_device_forceinline void volume_integrate_step_scattering(
ccl_private VolumeIntegrateState &ccl_restrict vstate,
ccl_private VolumeIntegrateResult &ccl_restrict result)
{
/* Pick random color channel, we use the Veach one-sample
* model with balance heuristic for the channels. */
const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t);
/* Pick random color channel for sampling the scatter distance. We use the Veach one-sample model
* with balance heuristic for the channels.
* Set `albedo` to 1 for the channel where extinction coefficient `sigma_t` is zero, to make sure
* that we sample a distance outside the current segment when that channel is picked, meaning
* light passes through without attenuation. */
const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t, 1.0f);
Spectrum channel_pdf;
const int channel = volume_sample_channel(
albedo, result.indirect_throughput, &vstate.rchannel, &channel_pdf);

View File

@@ -634,12 +634,13 @@ ccl_device_inline Spectrum safe_invert_color(Spectrum a)
return a;
}
ccl_device_inline Spectrum safe_divide_color(Spectrum a, Spectrum b)
/* Returns `a/b`, and replace the channel value with `fallback` if `b == 0`. */
ccl_device_inline Spectrum safe_divide_color(Spectrum a, Spectrum b, const float fallback = 0.0f)
{
FOREACH_SPECTRUM_CHANNEL (i) {
GET_SPECTRUM_CHANNEL(a, i) = (GET_SPECTRUM_CHANNEL(b, i) != 0.0f) ?
GET_SPECTRUM_CHANNEL(a, i) / GET_SPECTRUM_CHANNEL(b, i) :
0.0f;
fallback;
}
return a;