Fix T54420: too much volume render noise with multiple volume objects.

Random numbers for step offset were correlated, now use stratified samples
which reduces noise as well for some types of volumes, mainly procedural
ones where the step size is bigger than the volume features.
This commit is contained in:
Brecht Van Lommel 2018-03-26 12:13:36 +02:00
parent 3c45fdd171
commit a7aee250b8
Notes: blender-bot 2023-02-14 10:54:29 +01:00
Referenced by issue #56650, Volumes look much much different in master compared to 2.79b
Referenced by issue #54420, Blender cycles volumetric rendering bug
4 changed files with 61 additions and 36 deletions

View File

@ -60,8 +60,6 @@ ccl_device_inline void path_state_init(KernelGlobals *kg,
if(kernel_data.integrator.use_volumes) {
/* Initialize volume stack with volume we are inside of. */
kernel_volume_stack_init(kg, stack_sd, state, ray, state->volume_stack);
/* Seed RNG for cases where we can't use stratified samples .*/
state->rng_congruential = lcg_init(rng_hash + sample*0x51633e2d);
}
else {
state->volume_stack[0].shader = SHADER_NONE;

View File

@ -199,6 +199,19 @@ ccl_device_inline void path_state_rng_2D(KernelGlobals *kg,
fx, fy);
}
ccl_device_inline float path_state_rng_1D_hash(KernelGlobals *kg,
const ccl_addr_space PathState *state,
uint hash)
{
/* Use a hash instead of dimension, this is not great but avoids adding
* more dimensions to each bounce which reduces quality of dimensions we
* are already using. */
return path_rng_1D(kg,
cmj_hash_simple(state->rng_hash, hash),
state->sample, state->num_samples,
state->rng_offset);
}
ccl_device_inline float path_branched_rng_1D(
KernelGlobals *kg,
uint rng_hash,

View File

@ -1107,7 +1107,6 @@ typedef struct PathState {
#ifdef __VOLUME__
int volume_bounce;
int volume_bounds_bounce;
uint rng_congruential;
VolumeStack volume_stack[VOLUME_STACK_SIZE];
#endif
} PathState;

View File

@ -156,6 +156,24 @@ ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stac
return method;
}
ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg,
ccl_addr_space PathState *state,
float t,
float *step_size,
float *step_offset)
{
const int max_steps = kernel_data.integrator.volume_max_steps;
float step = min(kernel_data.integrator.volume_step_size, t);
/* compute exact steps in advance for malloc */
if(t > max_steps * step) {
step = t / (float)max_steps;
}
*step_size = step;
*step_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4) * step;
}
/* Volume Shadows
*
* These functions are used to attenuate shadow rays to lights. Both absorption
@ -188,8 +206,8 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
/* prepare for stepping */
int max_steps = kernel_data.integrator.volume_max_steps;
float step = kernel_data.integrator.volume_step_size;
float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step;
float step_offset, step_size;
kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
/* compute extinction at the start */
float t = 0.0f;
@ -198,14 +216,15 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
for(int i = 0; i < max_steps; i++) {
/* advance to new position */
float new_t = min(ray->t, (i+1) * step);
float dt = new_t - t;
float new_t = min(ray->t, (i+1) * step_size);
/* use random position inside this segment to sample shader */
if(new_t == ray->t)
random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
/* use random position inside this segment to sample shader, adjust
* for last step that is shorter than other steps. */
if(new_t == ray->t) {
step_offset *= (new_t - t) / step_size;
}
float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
float3 new_P = ray->P + ray->D * (t + step_offset);
float3 sigma_t;
/* compute attenuation over segment */
@ -504,8 +523,8 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
/* prepare for stepping */
int max_steps = kernel_data.integrator.volume_max_steps;
float step_size = kernel_data.integrator.volume_step_size;
float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step_size;
float step_offset, step_size;
kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
/* compute coefficients at the start */
float t = 0.0f;
@ -522,11 +541,13 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
float new_t = min(ray->t, (i+1) * step_size);
float dt = new_t - t;
/* use random position inside this segment to sample shader */
if(new_t == ray->t)
random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
/* use random position inside this segment to sample shader,
* for last shorter step we remap it to fit within the segment. */
if(new_t == ray->t) {
step_offset *= (new_t - t) / step_size;
}
float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
float3 new_P = ray->P + ray->D * (t + step_offset);
VolumeShaderCoefficients coeff;
/* compute segment */
@ -694,19 +715,12 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
/* prepare for volume stepping */
int max_steps;
float step_size, random_jitter_offset;
float step_size, step_offset;
if(heterogeneous) {
const int global_max_steps = kernel_data.integrator.volume_max_steps;
step_size = kernel_data.integrator.volume_step_size;
/* compute exact steps in advance for malloc */
if(ray->t > global_max_steps*step_size) {
max_steps = global_max_steps;
step_size = ray->t / (float)max_steps;
}
else {
max_steps = max((int)ceilf(ray->t/step_size), 1);
}
max_steps = kernel_data.integrator.volume_max_steps;
kernel_volume_step_init(kg, state, ray->t, &step_size, &step_offset);
#ifdef __KERNEL_CPU__
/* NOTE: For the branched path tracing it's possible to have direct
* and indirect light integration both having volume segments allocated.
@ -723,19 +737,18 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
sizeof(*kg->decoupled_volume_steps));
if(kg->decoupled_volume_steps[index] == NULL) {
kg->decoupled_volume_steps[index] =
(VolumeStep*)malloc(sizeof(VolumeStep)*global_max_steps);
(VolumeStep*)malloc(sizeof(VolumeStep)*max_steps);
}
segment->steps = kg->decoupled_volume_steps[index];
++kg->decoupled_volume_steps_index;
#else
segment->steps = (VolumeStep*)malloc(sizeof(VolumeStep)*max_steps);
#endif
random_jitter_offset = lcg_step_float(&state->rng_congruential) * step_size;
}
else {
max_steps = 1;
step_size = ray->t;
random_jitter_offset = 0.0f;
step_offset = 0.0f;
segment->steps = &segment->stack_step;
}
@ -757,11 +770,13 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
float new_t = min(ray->t, (i+1) * step_size);
float dt = new_t - t;
/* use random position inside this segment to sample shader */
if(heterogeneous && new_t == ray->t)
random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
/* use random position inside this segment to sample shader,
* for last shorter step we remap it to fit within the segment. */
if(new_t == ray->t) {
step_offset *= (new_t - t) / step_size;
}
float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
float3 new_P = ray->P + ray->D * (t + step_offset);
VolumeShaderCoefficients coeff;
/* compute segment */
@ -818,7 +833,7 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
step->accum_transmittance = accum_transmittance;
step->cdf_distance = cdf_distance;
step->t = new_t;
step->shade_t = t + random_jitter_offset;
step->shade_t = t + step_offset;
/* stop if at the end of the volume */
t = new_t;