[Bf-blender-cvs] [008cc625aaf] blender-v3.4-release: Fix T102250: Cycles path guiding issue with equiangular sampling

Sebastian Herholz noreply at git.blender.org
Thu Nov 24 17:54:49 CET 2022


Commit: 008cc625aaf798b76155eb9880d6f0ae16ff36c5
Author: Sebastian Herholz
Date:   Thu Nov 24 17:03:45 2022 +0100
Branches: blender-v3.4-release
https://developer.blender.org/rB008cc625aaf798b76155eb9880d6f0ae16ff36c5

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

===================================================================

M	intern/cycles/kernel/integrator/shade_volume.h
M	intern/cycles/kernel/integrator/volume_shader.h
M	intern/cycles/kernel/types.h

===================================================================

diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h
index a8324cda2dc..46d13f59c6a 100644
--- a/intern/cycles/kernel/integrator/shade_volume.h
+++ b/intern/cycles/kernel/integrator/shade_volume.h
@@ -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);
+  /* 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
-      /* 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);
+          kg, state, &sd, rand_phase_guiding, direct_P, ray->D, &result.direct_phases);
 #    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;
     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;
   }
 }
diff --git a/intern/cycles/kernel/integrator/volume_shader.h b/intern/cycles/kernel/integrator/volume_shader.h
index 0ff968723a1..e895d7a4cc7 100644
--- a/intern/cycles/kernel/integrator/volume_shader.h
+++ b/intern/cycles/kernel/integrator/volume_shader.h
@@ -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_devi

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list