Cycles: don't count volume boundaries as transparent bounces.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Wed, 28 Feb 2018 22:00:46 +0000 (23:00 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Thu, 1 Mar 2018 00:21:29 +0000 (01:21 +0100)
This is more important now that we will have tigther volume bounds that
we hit multiple times. It also avoids some noise due to RR previously
affecting these surfaces, which shouldn't have been the case and should
eventually be fixed for transparent BSDFs as well.

For non-volume scenes I found no performance impact on NVIDIA or AMD.
For volume scenes the noise decrease and fixed artifacts are worth the
little extra render time, when there is any.

intern/cycles/kernel/kernel_path.h
intern/cycles/kernel/kernel_path_branched.h
intern/cycles/kernel/kernel_path_state.h
intern/cycles/kernel/kernel_path_surface.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/split/kernel_next_iteration_setup.h
intern/cycles/kernel/split/kernel_queue_enqueue.h
intern/cycles/kernel/split/kernel_shader_setup.h

index aef350b065815e368cc010c1c270e1d9d77dc212..b0f53aef2d5f9dc5f243cb1cd10a7c28482d9073 100644 (file)
@@ -445,8 +445,15 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
                        break;
                }
 
-               /* Setup and evaluate shader. */
+               /* Setup shader data. */
                shader_setup_from_ray(kg, sd, &isect, ray);
+
+               /* Skip most work for volume bounding surface. */
+#ifdef __VOLUME__
+               if(!(sd->flag & SD_HAS_ONLY_VOLUME)) {
+#endif
+
+               /* Evaluate shader. */
                shader_eval_surface(kg, sd, state, state->flag);
                shader_prepare_closures(sd, state);
 
@@ -523,6 +530,10 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
                }
 #endif  /* defined(__EMISSION__) */
 
+#ifdef __VOLUME__
+               }
+#endif
+
                if(!kernel_path_surface_bounce(kg, sd, &throughput, state, &L->state, ray))
                        break;
        }
@@ -605,8 +616,15 @@ ccl_device_forceinline void kernel_path_integrate(
                        break;
                }
 
-               /* Setup and evaluate shader. */
+               /* Setup shader data. */
                shader_setup_from_ray(kg, &sd, &isect, ray);
+
+               /* Skip most work for volume bounding surface. */
+#ifdef __VOLUME__
+               if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
+#endif
+
+               /* Evaluate shader. */
                shader_eval_surface(kg, &sd, state, state->flag);
                shader_prepare_closures(&sd, state);
 
@@ -669,6 +687,10 @@ ccl_device_forceinline void kernel_path_integrate(
                /* direct lighting */
                kernel_path_surface_connect_light(kg, &sd, emission_sd, throughput, state, L);
 
+#ifdef __VOLUME__
+               }
+#endif
+
                /* compute direct lighting and next bounce */
                if(!kernel_path_surface_bounce(kg, &sd, &throughput, state, &L->state, ray))
                        break;
index 441a06eeba33850ff7a3d1eb227a51d47b4556f4..d43d418db29f56856e1812f29ef1c437c002e619 100644 (file)
@@ -480,6 +480,12 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
 
                /* Setup and evaluate shader. */
                shader_setup_from_ray(kg, &sd, &isect, &ray);
+
+               /* Skip most work for volume bounding surface. */
+#ifdef __VOLUME__
+               if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
+#endif
+
                shader_eval_surface(kg, &sd, &state, state.flag);
                shader_merge_closures(&sd);
 
@@ -533,37 +539,46 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
                }
 #endif  /* __SUBSURFACE__ */
 
-               if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
-                       PathState hit_state = state;
+               PathState hit_state = state;
 
 #ifdef __EMISSION__
-                       /* direct light */
-                       if(kernel_data.integrator.use_direct_light) {
-                               int all = (kernel_data.integrator.sample_all_lights_direct) ||
-                                         (state.flag & PATH_RAY_SHADOW_CATCHER);
-                               kernel_branched_path_surface_connect_light(kg,
-                                       &sd, emission_sd, &hit_state, throughput, 1.0f, L, all);
-                       }
+               /* direct light */
+               if(kernel_data.integrator.use_direct_light) {
+                       int all = (kernel_data.integrator.sample_all_lights_direct) ||
+                                         (state.flag & PATH_RAY_SHADOW_CATCHER);
+                       kernel_branched_path_surface_connect_light(kg,
+                               &sd, emission_sd, &hit_state, throughput, 1.0f, L, all);
+               }
 #endif  /* __EMISSION__ */
 
-                       /* indirect light */
-                       kernel_branched_path_surface_indirect_light(kg,
-                               &sd, &indirect_sd, emission_sd, throughput, 1.0f, &hit_state, L);
+               /* indirect light */
+               kernel_branched_path_surface_indirect_light(kg,
+                       &sd, &indirect_sd, emission_sd, throughput, 1.0f, &hit_state, L);
 
-                       /* continue in case of transparency */
-                       throughput *= shader_bsdf_transparency(kg, &sd);
+               /* continue in case of transparency */
+               throughput *= shader_bsdf_transparency(kg, &sd);
 
-                       if(is_zero(throughput))
-                               break;
-               }
+               if(is_zero(throughput))
+                       break;
 
                /* Update Path State */
                path_state_next(kg, &state, LABEL_TRANSPARENT);
 
+#ifdef __VOLUME__
+               }
+               else {
+                       /* For volume bounding meshes we pass through without counting transparent
+                        * bounces, only sanity check in case self intersection gets us stuck. */
+                       state.volume_bounds_bounce++;
+                       if (state.volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
+                               break;
+                       }
+               }
+#endif
+
                ray.P = ray_offset(sd.P, -sd.Ng);
                ray.t -= sd.ray_length; /* clipping works through transparent */
 
-
 #ifdef __RAY_DIFFERENTIALS__
                ray.dP = sd.dP;
                ray.dD.dx = -sd.dI.dx;
index 15d81fcddf43d97a5140ceef5872637387296f47..ff7d1307a6c35a2fc9b550f1e2d9704424787f28 100644 (file)
@@ -55,6 +55,7 @@ ccl_device_inline void path_state_init(KernelGlobals *kg,
 
 #ifdef __VOLUME__
        state->volume_bounce = 0;
+       state->volume_bounds_bounce = 0;
 
        if(kernel_data.integrator.use_volumes) {
                /* Initialize volume stack with volume we are inside of. */
index 7b566b01b04eba2dceadfa0392e644d57cb09389..bca346d5ee0a2dd5c3bdf58c5aace3c4b1196d07 100644 (file)
@@ -329,10 +329,12 @@ ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg,
        }
 #ifdef __VOLUME__
        else if(sd->flag & SD_HAS_ONLY_VOLUME) {
-               /* no surface shader but have a volume shader? act transparent */
-
-               /* update path state, count as transparent */
-               path_state_next(kg, state, LABEL_TRANSPARENT);
+               /* For volume bounding meshes we pass through without counting transparent
+                * bounces, only sanity check in case self intersection gets us stuck. */
+               state->volume_bounds_bounce++;
+               if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
+                       return false;
+               }
 
                if(state->bounce == 0)
                        ray->t -= sd->ray_length; /* clipping works through transparent */
index d967edca75dcc7ec549e75cb3c73e4ffd739d420..2a437cdbdc61d6a70c19a19e573f0f52c9eeeee5 100644 (file)
@@ -49,6 +49,8 @@ CCL_NAMESPACE_BEGIN
 #define BSSRDF_MAX_BOUNCES                     256
 #define LOCAL_MAX_HITS                         4
 
+#define VOLUME_BOUNDS_MAX       1024
+
 #define BECKMANN_TABLE_SIZE            256
 
 #define SHADER_NONE                            (~0)
@@ -1107,6 +1109,7 @@ typedef struct PathState {
        /* volume rendering */
 #ifdef __VOLUME__
        int volume_bounce;
+       int volume_bounds_bounce;
        uint rng_congruential;
        VolumeStack volume_stack[VOLUME_STACK_SIZE];
 #endif
@@ -1497,8 +1500,10 @@ enum RayState {
        RAY_ACTIVE,
        /* Denotes ray has completed processing all samples and is inactive. */
        RAY_INACTIVE,
-       /* Denoted ray has exited path-iteration and needs to update output buffer. */
+       /* Denotes ray has exited path-iteration and needs to update output buffer. */
        RAY_UPDATE_BUFFER,
+       /* Denotes ray needs to skip most surface shader work. */
+       RAY_HAS_ONLY_VOLUME,
        /* Donotes ray has hit background */
        RAY_HIT_BACKGROUND,
        /* Denotes ray has to be regenerated */
index 81024f0cf99d2668531030180abc12f067071455..8092419e796f87d657aa6f0f5be739a0d85781b8 100644 (file)
@@ -53,39 +53,52 @@ ccl_device_inline void kernel_split_branched_indirect_light_init(KernelGlobals *
        ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_LIGHT_INDIRECT);
 }
 
-ccl_device void kernel_split_branched_indirect_light_end(KernelGlobals *kg, int ray_index)
+ccl_device void kernel_split_branched_transparent_bounce(KernelGlobals *kg, int ray_index)
 {
-       kernel_split_branched_path_indirect_loop_end(kg, ray_index);
-
        ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index];
        ShaderData *sd = kernel_split_sd(sd, ray_index);
        ccl_global PathState *state = &kernel_split_state.path_state[ray_index];
        ccl_global Ray *ray = &kernel_split_state.ray[ray_index];
 
-       /* continue in case of transparency */
-       *throughput *= shader_bsdf_transparency(kg, sd);
+#  ifdef __VOLUME__
+       if(!(sd->flag & SD_HAS_ONLY_VOLUME)) {
+#  endif
+               /* continue in case of transparency */
+               *throughput *= shader_bsdf_transparency(kg, sd);
+
+               if(is_zero(*throughput)) {
+                       kernel_split_path_end(kg, ray_index);
+                       return;
+               }
 
-       if(is_zero(*throughput)) {
-               kernel_split_path_end(kg, ray_index);
-       }
-       else {
                /* Update Path State */
                path_state_next(kg, state, LABEL_TRANSPARENT);
+#  ifdef __VOLUME__
+       }
+       else {
+               /* For volume bounding meshes we pass through without counting transparent
+                * bounces, only sanity check in case self intersection gets us stuck. */
+               state->volume_bounds_bounce++;
+               if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) {
+                       kernel_split_path_end(kg, ray_index);
+                       return;
+               }
+       }
+#  endif
 
-               ray->P = ray_offset(sd->P, -sd->Ng);
-               ray->t -= sd->ray_length; /* clipping works through transparent */
+       ray->P = ray_offset(sd->P, -sd->Ng);
+       ray->t -= sd->ray_length; /* clipping works through transparent */
 
 #  ifdef __RAY_DIFFERENTIALS__
-               ray->dP = sd->dP;
-               ray->dD.dx = -sd->dI.dx;
-               ray->dD.dy = -sd->dI.dy;
+       ray->dP = sd->dP;
+       ray->dD.dx = -sd->dI.dx;
+       ray->dD.dy = -sd->dI.dy;
 #  endif  /* __RAY_DIFFERENTIALS__ */
 
 #  ifdef __VOLUME__
-               /* enter/exit volume */
-               kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
+       /* enter/exit volume */
+       kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
 #  endif  /* __VOLUME__ */
-       }
 }
 #endif  /* __BRANCHED_PATH__ */
 
@@ -121,6 +134,13 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
 
        ccl_global char *ray_state = kernel_split_state.ray_state;
 
+#  ifdef __VOLUME__
+       /* Reactivate only volume rays here, most surface work was skipped. */
+       if(IS_STATE(ray_state, ray_index, RAY_HAS_ONLY_VOLUME)) {
+               ASSIGN_RAY_STATE(ray_state, ray_index, RAY_ACTIVE);
+       }
+#  endif
+
        bool active = IS_STATE(ray_state, ray_index, RAY_ACTIVE);
        if(active) {
                ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index];
@@ -138,6 +158,9 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
                        }
 #ifdef __BRANCHED_PATH__
                }
+               else if(sd->flag & SD_HAS_ONLY_VOLUME) {
+                       kernel_split_branched_transparent_bounce(kg, ray_index);
+               }
                else {
                        kernel_split_branched_indirect_light_init(kg, ray_index);
 
@@ -151,7 +174,8 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
                                ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED);
                        }
                        else {
-                               kernel_split_branched_indirect_light_end(kg, ray_index);
+                               kernel_split_branched_path_indirect_loop_end(kg, ray_index);
+                               kernel_split_branched_transparent_bounce(kg, ray_index);
                        }
                }
 #endif  /* __BRANCHED_PATH__ */
@@ -196,7 +220,8 @@ ccl_device void kernel_next_iteration_setup(KernelGlobals *kg,
                        ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED);
                }
                else {
-                       kernel_split_branched_indirect_light_end(kg, ray_index);
+                       kernel_split_branched_path_indirect_loop_end(kg, ray_index);
+                       kernel_split_branched_transparent_bounce(kg, ray_index);
                }
        }
 
index 66ce2dfb6f14815a6824523f20087646e26bc3bc..df67fabab19b58d47f6fa38d28ce50be06bc5fdf 100644 (file)
@@ -56,6 +56,7 @@ ccl_device void kernel_queue_enqueue(KernelGlobals *kg,
                queue_number = QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS;
        }
        else if(IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE) ||
+               IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME) ||
                IS_STATE(kernel_split_state.ray_state, ray_index, RAY_REGENERATED)) {
                queue_number = QUEUE_ACTIVE_AND_REGENERATED_RAYS;
        }
index 9d428ee813990a4421e43da6fb07d4015f489cd4..ea3ec2ec83fe82a2f86dc480b813e7223fbae007 100644 (file)
@@ -59,12 +59,20 @@ ccl_device void kernel_shader_setup(KernelGlobals *kg,
        if(IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) {
                Intersection isect = kernel_split_state.isect[ray_index];
                Ray ray = kernel_split_state.ray[ray_index];
+               ShaderData *sd = kernel_split_sd(sd, ray_index);
 
                shader_setup_from_ray(kg,
-                                     kernel_split_sd(sd, ray_index),
+                                     sd,
                                      &isect,
                                      &ray);
+
+#ifdef __VOLUME__
+               if(sd->flag & SD_HAS_ONLY_VOLUME) {
+                       ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME);
+               }
+#endif
        }
+
 }
 
 CCL_NAMESPACE_END