Fix T56925: Cycles banding artifacts in dense volumes
Offset the starting point of segments by a random amount to avoid the bounding box shape affecting the result and creating artifacts. Differential Revision: https://developer.blender.org/D10576
This commit is contained in:
parent
cd3fade2aa
commit
fbe0165aad
|
@ -175,7 +175,8 @@ ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg,
|
|||
const float object_step_size,
|
||||
float t,
|
||||
float *step_size,
|
||||
float *step_offset)
|
||||
float *step_shade_offset,
|
||||
float *steps_offset)
|
||||
{
|
||||
const int max_steps = kernel_data.integrator.volume_max_steps;
|
||||
float step = min(object_step_size, t);
|
||||
|
@ -186,7 +187,14 @@ ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg,
|
|||
}
|
||||
|
||||
*step_size = step;
|
||||
*step_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4) * step;
|
||||
|
||||
/* Perform shading at this offset within a step, to integrate over
|
||||
* over the entire step segment. */
|
||||
*step_shade_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4);
|
||||
|
||||
/* Shift starting point of all segment by this random amount to avoid
|
||||
* banding artifacts from the volume bounding shape. */
|
||||
*steps_offset = path_state_rng_1D_hash(kg, state, 0x3d22c7b3);
|
||||
}
|
||||
|
||||
/* Volume Shadows
|
||||
|
@ -220,10 +228,15 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
|
|||
float3 tp = *throughput;
|
||||
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
|
||||
|
||||
/* prepare for stepping */
|
||||
/* Prepare for stepping.
|
||||
* For shadows we do not offset all segments, since the starting point is
|
||||
* already a random distance inside the volume. It also appears to create
|
||||
* banding artifacts for unknown reasons. */
|
||||
int max_steps = kernel_data.integrator.volume_max_steps;
|
||||
float step_offset, step_size;
|
||||
kernel_volume_step_init(kg, state, object_step_size, ray->t, &step_size, &step_offset);
|
||||
float step_size, step_shade_offset, unused;
|
||||
kernel_volume_step_init(
|
||||
kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &unused);
|
||||
const float steps_offset = 1.0f;
|
||||
|
||||
/* compute extinction at the start */
|
||||
float t = 0.0f;
|
||||
|
@ -232,23 +245,17 @@ 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_size);
|
||||
float new_t = min(ray->t, (i + steps_offset) * step_size);
|
||||
float dt = new_t - t;
|
||||
|
||||
/* 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 + step_offset);
|
||||
float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset);
|
||||
float3 sigma_t = zero_float3();
|
||||
|
||||
/* compute attenuation over segment */
|
||||
if (volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) {
|
||||
/* Compute expf() only for every Nth step, to save some calculations
|
||||
* because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */
|
||||
|
||||
sum += (-sigma_t * (new_t - t));
|
||||
sum += (-sigma_t * dt);
|
||||
if ((i & 0x07) == 0) { /* ToDo: Other interval? */
|
||||
tp = *throughput * exp3(sum);
|
||||
|
||||
|
@ -567,10 +574,12 @@ kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
|
|||
float3 tp = *throughput;
|
||||
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
|
||||
|
||||
/* prepare for stepping */
|
||||
/* Prepare for stepping.
|
||||
* Using a different step offset for the first step avoids banding artifacts. */
|
||||
int max_steps = kernel_data.integrator.volume_max_steps;
|
||||
float step_offset, step_size;
|
||||
kernel_volume_step_init(kg, state, object_step_size, ray->t, &step_size, &step_offset);
|
||||
float step_size, step_shade_offset, steps_offset;
|
||||
kernel_volume_step_init(
|
||||
kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &steps_offset);
|
||||
|
||||
/* compute coefficients at the start */
|
||||
float t = 0.0f;
|
||||
|
@ -584,16 +593,10 @@ kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
|
|||
|
||||
for (int i = 0; i < max_steps; i++) {
|
||||
/* advance to new position */
|
||||
float new_t = min(ray->t, (i + 1) * step_size);
|
||||
float new_t = min(ray->t, (i + steps_offset) * step_size);
|
||||
float dt = new_t - t;
|
||||
|
||||
/* 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 + step_offset);
|
||||
float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset);
|
||||
VolumeShaderCoefficients coeff ccl_optional_struct_init;
|
||||
|
||||
/* compute segment */
|
||||
|
@ -771,11 +774,12 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
|
|||
|
||||
/* prepare for volume stepping */
|
||||
int max_steps;
|
||||
float step_size, step_offset;
|
||||
float step_size, step_shade_offset, steps_offset;
|
||||
|
||||
if (object_step_size != FLT_MAX) {
|
||||
max_steps = kernel_data.integrator.volume_max_steps;
|
||||
kernel_volume_step_init(kg, state, object_step_size, ray->t, &step_size, &step_offset);
|
||||
kernel_volume_step_init(
|
||||
kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &steps_offset);
|
||||
|
||||
# ifdef __KERNEL_CPU__
|
||||
/* NOTE: For the branched path tracing it's possible to have direct
|
||||
|
@ -802,7 +806,8 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
|
|||
else {
|
||||
max_steps = 1;
|
||||
step_size = ray->t;
|
||||
step_offset = 0.0f;
|
||||
step_shade_offset = 0.0f;
|
||||
steps_offset = 1.0f;
|
||||
segment->steps = &segment->stack_step;
|
||||
}
|
||||
|
||||
|
@ -821,16 +826,10 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
|
|||
|
||||
for (int i = 0; i < max_steps; i++, step++) {
|
||||
/* advance to new position */
|
||||
float new_t = min(ray->t, (i + 1) * step_size);
|
||||
float new_t = min(ray->t, (i + steps_offset) * step_size);
|
||||
float dt = new_t - t;
|
||||
|
||||
/* 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 + step_offset);
|
||||
float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset);
|
||||
VolumeShaderCoefficients coeff ccl_optional_struct_init;
|
||||
|
||||
/* compute segment */
|
||||
|
@ -888,7 +887,7 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg,
|
|||
step->accum_transmittance = accum_transmittance;
|
||||
step->cdf_distance = cdf_distance;
|
||||
step->t = new_t;
|
||||
step->shade_t = t + step_offset;
|
||||
step->shade_t = t + dt * step_shade_offset;
|
||||
|
||||
/* stop if at the end of the volume */
|
||||
t = new_t;
|
||||
|
|
Loading…
Reference in New Issue