Commit: d88d07723709dec7e62ad5db2187cd451e9eb21c Author: Brecht Van Lommel Date: Wed Jul 7 21:01:54 2021 +0200 Branches: cycles-x https://developer.blender.org/rBd88d07723709dec7e62ad5db2187cd451e9eb21c
Cycles X: refactor to separately track direct and indirect in volumes So that we can scatter at two different positions. Ref D11845 =================================================================== M intern/cycles/kernel/integrator/integrator_shade_volume.h =================================================================== diff --git a/intern/cycles/kernel/integrator/integrator_shade_volume.h b/intern/cycles/kernel/integrator/integrator_shade_volume.h index 08457473cd8..4e4b6243615 100644 --- a/intern/cycles/kernel/integrator/integrator_shade_volume.h +++ b/intern/cycles/kernel/integrator/integrator_shade_volume.h @@ -31,6 +31,21 @@ typedef enum VolumeIntegrateEvent { VOLUME_PATH_MISSED = 2 } VolumeIntegrateEvent; +typedef struct VolumeIntegrateResult { + /* Throughput and offset for direct light scattering. */ + VolumeSampleMethod direct_sample_method; + bool direct_scatter; + float3 direct_throughput; + float direct_t; + ShaderVolumePhases direct_phases; + + /* Throughput and offset for indirect light scattering. */ + bool indirect_scatter; + float3 indirect_throughput; + float indirect_t; + ShaderVolumePhases indirect_phases; +} VolumeIntegrateResult; + /* Ignore paths that have volume throughput below this value, to avoid unnecessary work * and precision issues. * todo: this value could be tweaked or turned into a probability to avoid unnecessary @@ -366,24 +381,88 @@ ccl_device float3 volume_emission_integrate(VolumeShaderCoefficients *coeff, return emission; } -/* Volume Path */ +/* Volume Integration */ + +typedef struct VolumeIntegrateState { + /* Volume segment extents. */ + float start_t; + float end_t; + + /* Current throughput. */ + float3 throughput; + + /* If volume is absorption-only up to this point, and no probabilistic + * scattering or termination has been used yet. */ + bool absorption_only; + + /* Random numbers for scattering. */ + float rscatter; + float rphase; +} VolumeIntegrateState; + +ccl_device_forceinline void volume_integrate_step_scattering( + const ShaderData *sd, + const VolumeShaderCoefficients &ccl_restrict coeff, + const float3 transmittance, + VolumeIntegrateState &ccl_restrict vstate, + VolumeIntegrateResult &ccl_restrict result) +{ + /* Distance sampling */ + + /* Pick random color channel, we use the Veach one-sample + * model with balance heuristic for the channels. */ + const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); + float3 channel_pdf; + const int channel = volume_sample_channel( + albedo, result.indirect_throughput, vstate.rphase, &channel_pdf); + + /* decide if we will scatter or continue */ + const float sample_transmittance = volume_channel_get(transmittance, channel); + + if (1.0f - vstate.rscatter >= sample_transmittance) { + /* compute sampling distance */ + const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel); + const float new_dt = -logf(1.0f - vstate.rscatter) / sample_sigma_t; + const float new_t = vstate.start_t + new_dt; + + /* transmittance and pdf */ + const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); + const float3 pdf = coeff.sigma_t * new_transmittance; + + /* throughput */ + result.indirect_scatter = true; + result.indirect_t = new_t; + result.indirect_throughput *= coeff.sigma_s * new_transmittance / dot(channel_pdf, pdf); + shader_copy_volume_phases(&result.indirect_phases, sd); + + result.direct_scatter = true; + result.direct_t = result.indirect_t; + result.direct_throughput = result.indirect_throughput; + shader_copy_volume_phases(&result.direct_phases, sd); /* TODO: only copy once? */ + } + else { + /* throughput */ + const float pdf = dot(channel_pdf, transmittance); + result.indirect_throughput *= transmittance / pdf; + + /* remap rscatter so we can reuse it and keep thing stratified */ + vstate.rscatter = 1.0f - (1.0f - vstate.rscatter) / sample_transmittance; + } +} /* heterogeneous volume distance sampling: integrate stepping through the * volume until we reach the end, get absorbed entirely, or run out of * iterations. this does probabilistically scatter or get transmitted through * for path tracing where we don't want to branch. */ -ccl_device VolumeIntegrateEvent -volume_integrate_heterogeneous(INTEGRATOR_STATE_ARGS, - Ray *ccl_restrict ray, - ShaderData *ccl_restrict sd, - ShaderVolumePhases *ccl_restrict phases, - ccl_addr_space float3 *ccl_restrict throughput, - const RNGState *rng_state, - ccl_global float *ccl_restrict render_buffer, - const float object_step_size) +ccl_device_forceinline void volume_integrate_heterogeneous(INTEGRATOR_STATE_ARGS, + Ray *ccl_restrict ray, + ShaderData *ccl_restrict sd, + const RNGState *rng_state, + ccl_global float *ccl_restrict + render_buffer, + const float object_step_size, + VolumeIntegrateResult &result) { - float3 tp = *throughput; - /* Prepare for stepping. * Using a different step offset for the first step avoids banding artifacts. */ int max_steps; @@ -397,123 +476,86 @@ volume_integrate_heterogeneous(INTEGRATOR_STATE_ARGS, &steps_offset, &max_steps); - /* compute coefficients at the start */ - float t = 0.0f; - float3 accum_transmittance = one_float3(); + /* Initialize volume integration state. */ + VolumeIntegrateState vstate ccl_optional_struct_init; + vstate.start_t = 0.0f; + vstate.end_t = 0.0f; + vstate.absorption_only = true; + vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_SCATTER_DISTANCE); + vstate.rphase = path_state_rng_1D(kg, rng_state, PRNG_PHASE_CHANNEL); - /* pick random color channel, we use the Veach one-sample - * model with balance heuristic for the channels */ - float xi = path_state_rng_1D(kg, rng_state, PRNG_SCATTER_DISTANCE); - const float rphase = path_state_rng_1D(kg, rng_state, PRNG_PHASE_CHANNEL); - bool has_scatter = false; + /* Initialize volume integration result. */ + const float3 throughput = INTEGRATOR_STATE(path, throughput); + result.direct_throughput = throughput; + result.indirect_throughput = throughput; for (int i = 0; i < max_steps; i++) { - /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); - float dt = new_t - t; - - float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - VolumeShaderCoefficients coeff ccl_optional_struct_init; + /* Advance to new position */ + vstate.end_t = min(ray->t, (i + steps_offset) * step_size); + const float shade_t = vstate.start_t + (vstate.end_t - vstate.start_t) * step_shade_offset; + sd->P = ray->P + ray->D * shade_t; /* compute segment */ - sd->P = new_P; + VolumeShaderCoefficients coeff ccl_optional_struct_init; if (volume_shader_sample(INTEGRATOR_STATE_PASS, sd, &coeff)) { - int closure_flag = sd->flag; - float3 new_tp; - float3 transmittance; - bool scatter = false; - - /* distance sampling */ - if ((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_EXTINCTION))) { - has_scatter = true; - - /* Sample channel, use MIS with balance heuristic. */ - const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); - float3 channel_pdf; - const int channel = volume_sample_channel(albedo, tp, rphase, &channel_pdf); - - /* compute transmittance over full step */ - transmittance = volume_color_transmittance(coeff.sigma_t, dt); - - /* decide if we will scatter or continue */ - const float sample_transmittance = volume_channel_get(transmittance, channel); - - if (1.0f - xi >= sample_transmittance) { - /* compute sampling distance */ - const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel); - const float new_dt = -logf(1.0f - xi) / sample_sigma_t; - new_t = t + new_dt; - - /* transmittance and pdf */ - const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); - const float3 pdf = coeff.sigma_t * new_transmittance; - - /* throughput */ - new_tp = tp * coeff.sigma_s * new_transmittance / dot(channel_pdf, pdf); - scatter = true; - } - else { - /* throughput */ - const float pdf = dot(channel_pdf, transmittance); - new_tp = tp * transmittance / pdf; + const int closure_flag = sd->flag; - /* remap xi so we can reuse it and keep thing stratified */ - xi = 1.0f - (1.0f - xi) / sample_transmittance; - } - } - else if (closure_flag & SD_EXTINCTION) { - /* absorption only, no sampling needed */ - transmittance = volume_color_transmittance(coeff.sigma_t, dt); - new_tp = tp * transmittance; - } - else { - transmittance = zero_float3(); - new_tp = tp; - } + /* Evaluate transmittance over segment. */ + const float dt = (vstate.end_t - vstate.start_t); + const float3 transmittance = (closure_flag & SD_EXTINCTION) ? + volume_color_transmittance(coeff.sigma_t, dt) : + one_float3(); - /* integrate emission attenuated by absorption */ + /* Emission. */ if (closure_flag & SD_EMISSION) { - const float3 emission = volume_emission_integrate(&coeff, closure_flag, transmittance, dt); - kernel_accum_emission(INTEGRATOR_STATE_PASS, tp, emission, render_buffer); + /* Only write emission before indirect light scatter position, since we terminate + * stepping at that point if we have already found a direct light scatter position. */ + if (!result.indirect_scatter) { + /* TODO: write only once to avoid overhead of atomics? */ + const float3 emission = volume_ @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs