Fix T102250: Cycles path guiding issue with equiangular sampling
The wrong guiding distribution was used when direct and indirect light scattering happened at different locations. Now use a different distribution for each location. Recording is not quite correct since OpenPGL does not support spliting the path like this, instead recording at the start of the volume ray. In practice this seems to make little difference. Differential Revision: https://developer.blender.org/D16448
This commit is contained in:
parent
38c4f40159
commit
008cc625aa
Notes:
blender-bot
2023-02-14 06:00:49 +01:00
Referenced by issue #102250, Path guiding in volumes do not fully support equiangular sampling
|
@ -34,6 +34,9 @@ typedef struct VolumeIntegrateResult {
|
|||
Spectrum direct_throughput;
|
||||
float direct_t;
|
||||
ShaderVolumePhases direct_phases;
|
||||
# ifdef __PATH_GUIDING__
|
||||
VolumeSampleMethod direct_sample_method;
|
||||
# endif
|
||||
|
||||
/* Throughput and offset for indirect light scattering. */
|
||||
bool indirect_scatter;
|
||||
|
@ -580,6 +583,9 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
|
|||
result.direct_t = volume_equiangular_sample(
|
||||
ray, equiangular_light_P, vstate.rscatter, &vstate.equiangular_pdf);
|
||||
}
|
||||
# ifdef __PATH_GUIDING__
|
||||
result.direct_sample_method = vstate.direct_sample_method;
|
||||
# endif
|
||||
|
||||
# ifdef __DENOISING_FEATURES__
|
||||
const bool write_denoising_features = (INTEGRATOR_STATE(state, path, flag) &
|
||||
|
@ -719,6 +725,9 @@ ccl_device_forceinline void integrate_volume_direct_light(
|
|||
ccl_private const RNGState *ccl_restrict rng_state,
|
||||
const float3 P,
|
||||
ccl_private const ShaderVolumePhases *ccl_restrict phases,
|
||||
# ifdef __PATH_GUIDING__
|
||||
ccl_private const Spectrum unlit_throughput,
|
||||
# endif
|
||||
ccl_private const Spectrum throughput,
|
||||
ccl_private LightSample *ccl_restrict ls)
|
||||
{
|
||||
|
@ -851,7 +860,7 @@ ccl_device_forceinline void integrate_volume_direct_light(
|
|||
kernel_data.background.lightgroup + 1;
|
||||
|
||||
# ifdef __PATH_GUIDING__
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = throughput;
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unlit_throughput) = unlit_throughput;
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, path_segment) = INTEGRATOR_STATE(
|
||||
state, guiding, path_segment);
|
||||
# endif
|
||||
|
@ -990,7 +999,13 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||
const float step_size = volume_stack_step_size(kg, volume_read_lambda_pass);
|
||||
|
||||
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
||||
/* The current path throughput which is used later to calculate per-segment throughput.*/
|
||||
const float3 initial_throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
/* The path throughput used to calculate the throughput for direct light. */
|
||||
float3 unlit_throughput = initial_throughput;
|
||||
/* If a new path segment is generated at the direct scatter position.*/
|
||||
bool guiding_generated_new_segment = false;
|
||||
float rand_phase_guiding = 0.5f;
|
||||
# endif
|
||||
|
||||
/* TODO: expensive to zero closures? */
|
||||
|
@ -1018,41 +1033,48 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||
return VOLUME_PATH_MISSED;
|
||||
}
|
||||
|
||||
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
||||
bool guiding_generated_new_segment = false;
|
||||
if (kernel_data.integrator.use_guiding) {
|
||||
/* Record transmittance using change in throughput. */
|
||||
float3 transmittance_weight = spectrum_to_rgb(
|
||||
safe_divide_color(result.indirect_throughput, initial_throughput));
|
||||
guiding_record_volume_transmission(kg, state, transmittance_weight);
|
||||
|
||||
if (result.indirect_scatter) {
|
||||
const float3 P = ray->P + result.indirect_t * ray->D;
|
||||
|
||||
/* Record volume segment up to direct scatter position.
|
||||
* TODO: volume segment is wrong when direct_t and indirect_t. */
|
||||
if (result.direct_scatter && (result.direct_t == result.indirect_t)) {
|
||||
guiding_record_volume_segment(kg, state, P, sd.I);
|
||||
guiding_generated_new_segment = true;
|
||||
}
|
||||
|
||||
# if PATH_GUIDING_LEVEL >= 4
|
||||
/* TODO: this position will be wrong for direct light pdf computation,
|
||||
* since the direct light position may be different? */
|
||||
volume_shader_prepare_guiding(
|
||||
kg, state, &sd, &rng_state, P, ray->D, &result.direct_phases, direct_sample_method);
|
||||
# endif
|
||||
}
|
||||
else {
|
||||
/* No guiding if we don't scatter. */
|
||||
state->guiding.use_volume_guiding = false;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Direct light. */
|
||||
if (result.direct_scatter) {
|
||||
const float3 direct_P = ray->P + result.direct_t * ray->D;
|
||||
|
||||
# ifdef __PATH_GUIDING__
|
||||
if (kernel_data.integrator.use_guiding) {
|
||||
# if PATH_GUIDING_LEVEL >= 1
|
||||
if (result.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
|
||||
/* If the direct scatter event is generated using VOLUME_SAMPLE_DISTANCE the direct event
|
||||
* will happen at the same position as the indirect event and the direct light contribution
|
||||
* will contribute to the position of the next path segment.*/
|
||||
float3 transmittance_weight = spectrum_to_rgb(
|
||||
safe_divide_color(result.indirect_throughput, initial_throughput));
|
||||
guiding_record_volume_transmission(kg, state, transmittance_weight);
|
||||
guiding_record_volume_segment(kg, state, direct_P, sd.I);
|
||||
guiding_generated_new_segment = true;
|
||||
unlit_throughput = result.indirect_throughput / continuation_probability;
|
||||
rand_phase_guiding = path_state_rng_1D(kg, &rng_state, PRNG_VOLUME_PHASE_GUIDING_DISTANCE);
|
||||
}
|
||||
else {
|
||||
/* If the direct scatter event is generated using VOLUME_SAMPLE_EQUIANGULAR the direct
|
||||
* event will happen at a separate position as the indirect event and the direct light
|
||||
* contribution will contribute to the position of the current/previous path segment. The
|
||||
* unlit_throughput has to be adjusted to include the scattering at the previous segment.*/
|
||||
float3 scatterEval = one_float3();
|
||||
if (state->guiding.path_segment) {
|
||||
pgl_vec3f scatteringWeight = state->guiding.path_segment->scatteringWeight;
|
||||
scatterEval = make_float3(scatteringWeight.x, scatteringWeight.y, scatteringWeight.z);
|
||||
}
|
||||
unlit_throughput /= scatterEval;
|
||||
unlit_throughput *= continuation_probability;
|
||||
rand_phase_guiding = path_state_rng_1D(
|
||||
kg, &rng_state, PRNG_VOLUME_PHASE_GUIDING_EQUIANGULAR);
|
||||
}
|
||||
# endif
|
||||
# if PATH_GUIDING_LEVEL >= 4
|
||||
volume_shader_prepare_guiding(
|
||||
kg, state, &sd, rand_phase_guiding, direct_P, ray->D, &result.direct_phases);
|
||||
# endif
|
||||
}
|
||||
# endif
|
||||
|
||||
result.direct_throughput /= continuation_probability;
|
||||
integrate_volume_direct_light(kg,
|
||||
state,
|
||||
|
@ -1060,6 +1082,9 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||
&rng_state,
|
||||
direct_P,
|
||||
&result.direct_phases,
|
||||
# ifdef __PATH_GUIDING__
|
||||
unlit_throughput,
|
||||
# endif
|
||||
result.direct_throughput,
|
||||
&ls);
|
||||
}
|
||||
|
@ -1069,6 +1094,13 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||
* Only divide throughput by continuation_probability if we scatter. For the attenuation
|
||||
* case the next surface will already do this division. */
|
||||
if (result.indirect_scatter) {
|
||||
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
||||
if (!guiding_generated_new_segment) {
|
||||
float3 transmittance_weight = spectrum_to_rgb(
|
||||
safe_divide_color(result.indirect_throughput, initial_throughput));
|
||||
guiding_record_volume_transmission(kg, state, transmittance_weight);
|
||||
}
|
||||
# endif
|
||||
result.indirect_throughput /= continuation_probability;
|
||||
}
|
||||
INTEGRATOR_STATE_WRITE(state, path, throughput) = result.indirect_throughput;
|
||||
|
@ -1076,10 +1108,21 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||
if (result.indirect_scatter) {
|
||||
sd.P = ray->P + result.indirect_t * ray->D;
|
||||
|
||||
# if defined(__PATH_GUIDING__) && PATH_GUIDING_LEVEL >= 1
|
||||
# if defined(__PATH_GUIDING__)
|
||||
# if PATH_GUIDING_LEVEL >= 1
|
||||
if (!guiding_generated_new_segment) {
|
||||
guiding_record_volume_segment(kg, state, sd.P, sd.I);
|
||||
}
|
||||
# endif
|
||||
# if PATH_GUIDING_LEVEL >= 4
|
||||
/* If the direct scatter event was generated using VOLUME_SAMPLE_EQUIANGULAR we need to
|
||||
* initialize the guiding distribution at the indirect scatter position. */
|
||||
if (result.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
|
||||
rand_phase_guiding = path_state_rng_1D(kg, &rng_state, PRNG_VOLUME_PHASE_GUIDING_DISTANCE);
|
||||
volume_shader_prepare_guiding(
|
||||
kg, state, &sd, rand_phase_guiding, sd.P, ray->D, &result.indirect_phases);
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
|
||||
if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) {
|
||||
|
@ -1090,6 +1133,10 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
|
|||
}
|
||||
}
|
||||
else {
|
||||
# if defined(__PATH_GUIDING__)
|
||||
/* No guiding if we don't scatter. */
|
||||
state->guiding.use_volume_guiding = false;
|
||||
# endif
|
||||
return VOLUME_PATH_ATTENUATED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,11 +95,10 @@ ccl_device_inline void volume_shader_copy_phases(ccl_private ShaderVolumePhases
|
|||
ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
|
||||
IntegratorState state,
|
||||
ccl_private ShaderData *sd,
|
||||
ccl_private const RNGState *rng_state,
|
||||
float rand_phase_guiding,
|
||||
const float3 P,
|
||||
const float3 D,
|
||||
ccl_private ShaderVolumePhases *phases,
|
||||
const VolumeSampleMethod direct_sample_method)
|
||||
ccl_private ShaderVolumePhases *phases)
|
||||
{
|
||||
/* Have any phase functions to guide? */
|
||||
const int num_phases = phases->num_closure;
|
||||
|
@ -109,7 +108,6 @@ ccl_device_inline void volume_shader_prepare_guiding(KernelGlobals kg,
|
|||
}
|
||||
|
||||
const float volume_guiding_probability = kernel_data.integrator.volume_guiding_probability;
|
||||
float rand_phase_guiding = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_PHASE_GUIDING);
|
||||
|
||||
/* If we have more than one phase function we select one random based on its
|
||||
* sample weight to calculate the product distribution for guiding. */
|
||||
|
|
|
@ -156,7 +156,8 @@ enum PathTraceDimension {
|
|||
PRNG_VOLUME_SCATTER_DISTANCE = 5,
|
||||
PRNG_VOLUME_OFFSET = 6,
|
||||
PRNG_VOLUME_SHADE_OFFSET = 7,
|
||||
PRNG_VOLUME_PHASE_GUIDING = 8,
|
||||
PRNG_VOLUME_PHASE_GUIDING_DISTANCE = 8,
|
||||
PRNG_VOLUME_PHASE_GUIDING_EQUIANGULAR = 9,
|
||||
|
||||
/* Subsurface random walk bounces */
|
||||
PRNG_SUBSURFACE_BSDF = 0,
|
||||
|
|
Loading…
Reference in New Issue