Cycles code refactor: deduplicate and symmetrize some path tracing code.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Fri, 4 Apr 2014 12:21:35 +0000 (14:21 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Sat, 14 Jun 2014 11:49:56 +0000 (13:49 +0200)
intern/cycles/kernel/kernel_bake.h
intern/cycles/kernel/kernel_path.h

index e8845e03acbb001c6d265fa457eeb95b7128800e..a2ff6a05706c92e1ee1738f59b50501516f73845 100644 (file)
@@ -69,7 +69,9 @@ ccl_device void compute_light_pass(KernelGlobals *kg, ShaderData *sd, PathRadian
                                path_radiance_accum_emission(&L_sample, throughput, emission, state.bounce);
                        }
 
-                       if(kernel_path_integrate_lighting(kg, &rng, sd, &throughput, &state, &L_sample, &ray)) {
+                       kernel_path_surface_connect_light(kg, &rng, sd, throughput, &state, &L_sample);
+
+                       if(kernel_path_surface_bounce(kg, &rng, sd, &throughput, &state, &L_sample, &ray)) {
 #ifdef __LAMP_MIS__
                                state.ray_t = 0.0f;
 #endif
@@ -107,7 +109,17 @@ ccl_device void compute_light_pass(KernelGlobals *kg, ShaderData *sd, PathRadian
                                path_radiance_accum_emission(&L_sample, throughput, emission, state.bounce);
                        }
 
-                       kernel_branched_path_integrate_lighting(kg, &rng,
+#if defined(__EMISSION__)
+                       /* direct light */
+                       if(kernel_data.integrator.use_direct_light) {
+                               bool all = kernel_data.integrator.sample_all_lights_direct;
+                               kernel_branched_path_surface_connect_light(kg, &rng,
+                                       sd, &state, throughput, 1.0f, &L_sample, all);
+                       }
+#endif
+
+                       /* indirect light */
+                       kernel_branched_path_surface_indirect_light(kg, &rng,
                                sd, throughput, 1.0f, &state, &L_sample);
                }
        }
index 70366eaac908fd136d426dc606d2d0269c2f7118..f5606c003d1401e150e6c53325f4daf96f32cc24 100644 (file)
@@ -47,40 +47,45 @@ CCL_NAMESPACE_BEGIN
 
 #ifdef __VOLUME__
 
-ccl_device_inline bool kernel_path_integrate_scatter_lighting(KernelGlobals *kg, RNG *rng,
-       ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray,
-       float num_samples_adjust)
+ccl_device_inline void kernel_path_volume_connect_light(KernelGlobals *kg, RNG *rng,
+       ShaderData *sd, float3 throughput, PathState *state, PathRadiance *L, float num_samples_adjust)
 {
 #ifdef __EMISSION__
-       if(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) {
-               /* sample illumination from lights to find path contribution */
-               float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
-               float light_u, light_v;
-               path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+       if(!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)))
+               return;
 
-               Ray light_ray;
-               BsdfEval L_light;
-               bool is_lamp;
+       /* sample illumination from lights to find path contribution */
+       float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
+       float light_u, light_v;
+       path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+
+       Ray light_ray;
+       BsdfEval L_light;
+       bool is_lamp;
 
 #ifdef __OBJECT_MOTION__
-               light_ray.time = sd->time;
+       light_ray.time = sd->time;
 #endif
 
-               LightSample ls;
-               light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls);
+       LightSample ls;
+       light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls);
 
-               if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
-                       /* trace shadow ray */
-                       float3 shadow;
+       if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+               /* trace shadow ray */
+               float3 shadow;
 
-                       if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
-                               /* accumulate */
-                               path_radiance_accum_light(L, *throughput * num_samples_adjust, &L_light, shadow, 1.0f, state->bounce, is_lamp);
-                       }
+               if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+                       /* accumulate */
+                       path_radiance_accum_light(L, throughput * num_samples_adjust, &L_light, shadow, 1.0f, state->bounce, is_lamp);
                }
        }
 #endif
+}
 
+ccl_device_inline bool kernel_path_volume_bounce(KernelGlobals *kg, RNG *rng,
+       ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray,
+       float num_samples_adjust)
+{
        /* sample phase function */
        float phase_pdf;
        BsdfEval phase_eval;
@@ -126,7 +131,8 @@ ccl_device_inline bool kernel_path_integrate_scatter_lighting(KernelGlobals *kg,
 
 #if defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__)
 
-ccl_device void kernel_branched_path_integrate_direct_lighting(KernelGlobals *kg, RNG *rng,
+/* branched path tracing: connect path directly to position on one or more lights and add it to L */
+ccl_device void kernel_branched_path_surface_connect_light(KernelGlobals *kg, RNG *rng,
        ShaderData *sd, PathState *state, float3 throughput, float num_samples_adjust, PathRadiance *L, bool sample_all_lights)
 {
        /* sample illumination from lights to find path contribution */
@@ -224,6 +230,122 @@ ccl_device void kernel_branched_path_integrate_direct_lighting(KernelGlobals *kg
 
 #endif
 
+/* path tracing: connect path directly to position on a light and add it to L */
+ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, RNG *rng,
+       ShaderData *sd, float3 throughput, PathState *state, PathRadiance *L)
+{
+#ifdef __EMISSION__
+       if(!(kernel_data.integrator.use_direct_light) && (sd->flag & SD_BSDF_HAS_EVAL))
+               return;
+
+       float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
+       float light_u, light_v;
+       path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
+
+       Ray light_ray;
+       BsdfEval L_light;
+       bool is_lamp;
+
+#ifdef __OBJECT_MOTION__
+       light_ray.time = sd->time;
+#endif
+
+       LightSample ls;
+       light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls);
+
+       if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
+               /* trace shadow ray */
+               float3 shadow;
+
+               if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
+                       /* accumulate */
+                       path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
+               }
+       }
+#endif
+}
+
+/* path tracing: bounce off or through surface to with new direction stored in ray */
+ccl_device_inline bool kernel_path_surface_bounce(KernelGlobals *kg, RNG *rng,
+       ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray)
+{
+       /* no BSDF? we can stop here */
+       if(sd->flag & SD_BSDF) {
+               /* sample BSDF */
+               float bsdf_pdf;
+               BsdfEval bsdf_eval;
+               float3 bsdf_omega_in;
+               differential3 bsdf_domega_in;
+               float bsdf_u, bsdf_v;
+               path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
+               int label;
+
+               label = shader_bsdf_sample(kg, sd, bsdf_u, bsdf_v, &bsdf_eval,
+                       &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+
+               if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
+                       return false;
+
+               /* modify throughput */
+               path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
+
+               /* set labels */
+               if(!(label & LABEL_TRANSPARENT)) {
+                       state->ray_pdf = bsdf_pdf;
+#ifdef __LAMP_MIS__
+                       state->ray_t = 0.0f;
+#endif
+                       state->min_ray_pdf = fminf(bsdf_pdf, state->min_ray_pdf);
+               }
+
+               /* update path state */
+               path_state_next(kg, state, label);
+
+               /* setup ray */
+               ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
+               ray->D = bsdf_omega_in;
+
+               if(state->bounce == 0)
+                       ray->t -= sd->ray_length; /* clipping works through transparent */
+               else
+                       ray->t = FLT_MAX;
+
+#ifdef __RAY_DIFFERENTIALS__
+               ray->dP = sd->dP;
+               ray->dD = bsdf_domega_in;
+#endif
+
+#ifdef __VOLUME__
+               /* enter/exit volume */
+               if(label & LABEL_TRANSMIT)
+                       kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
+#endif
+               return true;
+       }
+#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);
+
+               /* setup ray position, direction stays unchanged */
+               ray->P = ray_offset(sd->P, -sd->Ng);
+#ifdef __RAY_DIFFERENTIALS__
+               ray->dP = sd->dP;
+#endif
+
+               /* enter/exit volume */
+               kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
+               return true;
+       }
+#endif
+       else {
+               /* no bsdf or volume? */
+               return false;
+       }
+}
+
 ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray,
        float3 throughput, int num_samples, PathState state, PathRadiance *L)
 {
@@ -270,7 +392,9 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray,
                                &volume_sd, &volume_ray, L, &throughput, rng);
 
                        if(result == VOLUME_PATH_SCATTERED) {
-                               if(kernel_path_integrate_scatter_lighting(kg, rng, &volume_sd, &throughput, &state, L, &ray, 1.0f))
+                               kernel_path_volume_connect_light(kg, rng, &volume_sd, throughput, &state, L, 1.0f);
+
+                               if(kernel_path_volume_bounce(kg, rng, &volume_sd, &throughput, &state, L, &ray, 1.0f))
                                        continue;
                                else
                                        break;
@@ -393,190 +517,12 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, Ray ray,
 #if defined(__EMISSION__) && defined(__BRANCHED_PATH__)
                if(kernel_data.integrator.use_direct_light) {
                        bool all = kernel_data.integrator.sample_all_lights_indirect;
-                       kernel_branched_path_integrate_direct_lighting(kg, rng, &sd, &state, throughput, 1.0f, L, all);
-               }
-#endif
-
-               /* no BSDF? we can stop here */
-               if(sd.flag & SD_BSDF) {
-                       /* sample BSDF */
-                       float bsdf_pdf;
-                       BsdfEval bsdf_eval;
-                       float3 bsdf_omega_in;
-                       differential3 bsdf_domega_in;
-                       float bsdf_u, bsdf_v;
-                       path_state_rng_2D(kg, rng, &state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-                       int label;
-
-                       label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
-                               &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
-                       if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
-                               break;
-
-                       /* modify throughput */
-                       path_radiance_bsdf_bounce(L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
-
-                       /* set labels */
-                       if(!(label & LABEL_TRANSPARENT)) {
-                               state.ray_pdf = bsdf_pdf;
-#ifdef __LAMP_MIS__
-                               state.ray_t = 0.0f;
-#endif
-                               state.min_ray_pdf = fminf(bsdf_pdf, state.min_ray_pdf);
-                       }
-
-                       /* update path state */
-                       path_state_next(kg, &state, label);
-
-                       /* setup ray */
-                       ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
-                       ray.D = bsdf_omega_in;
-                       ray.t = FLT_MAX;
-#ifdef __RAY_DIFFERENTIALS__
-                       ray.dP = sd.dP;
-                       ray.dD = bsdf_domega_in;
-#endif
-
-#ifdef __VOLUME__
-                       /* enter/exit volume */
-                       if(label & LABEL_TRANSMIT)
-                               kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
-#endif
+                       kernel_branched_path_surface_connect_light(kg, rng, &sd, &state, throughput, 1.0f, L, all);
                }
-#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);
-
-                       /* setup ray position, direction stays unchanged */
-                       ray.P = ray_offset(sd.P, -sd.Ng);
-#ifdef __RAY_DIFFERENTIALS__
-                       ray.dP = sd.dP;
 #endif
 
-                       /* enter/exit volume */
-                       kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
-               }
-#endif
-               else {
-                       /* no bsdf or volume? we're done */
+               if(!kernel_path_surface_bounce(kg, rng, &sd, &throughput, &state, L, &ray))
                        break;
-               }
-       }
-}
-
-ccl_device_inline bool kernel_path_integrate_lighting(KernelGlobals *kg, RNG *rng,
-       ShaderData *sd, float3 *throughput, PathState *state, PathRadiance *L, Ray *ray)
-{
-#ifdef __EMISSION__
-       if(kernel_data.integrator.use_direct_light) {
-               /* sample illumination from lights to find path contribution */
-               if(sd->flag & SD_BSDF_HAS_EVAL) {
-                       float light_t = path_state_rng_1D(kg, rng, state, PRNG_LIGHT);
-                       float light_u, light_v;
-                       path_state_rng_2D(kg, rng, state, PRNG_LIGHT_U, &light_u, &light_v);
-
-                       Ray light_ray;
-                       BsdfEval L_light;
-                       bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
-                       light_ray.time = sd->time;
-#endif
-
-                       LightSample ls;
-                       light_sample(kg, light_t, light_u, light_v, sd->time, sd->P, &ls);
-
-                       if(direct_emission(kg, sd, &ls, &light_ray, &L_light, &is_lamp, state->bounce, state->transparent_bounce)) {
-                               /* trace shadow ray */
-                               float3 shadow;
-
-                               if(!shadow_blocked(kg, state, &light_ray, &shadow)) {
-                                       /* accumulate */
-                                       path_radiance_accum_light(L, *throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
-                               }
-                       }
-               }
-       }
-#endif
-
-       /* no BSDF? we can stop here */
-       if(sd->flag & SD_BSDF) {
-               /* sample BSDF */
-               float bsdf_pdf;
-               BsdfEval bsdf_eval;
-               float3 bsdf_omega_in;
-               differential3 bsdf_domega_in;
-               float bsdf_u, bsdf_v;
-               path_state_rng_2D(kg, rng, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-               int label;
-
-               label = shader_bsdf_sample(kg, sd, bsdf_u, bsdf_v, &bsdf_eval,
-                       &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
-               if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
-                       return false;
-
-               /* modify throughput */
-               path_radiance_bsdf_bounce(L, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label);
-
-               /* set labels */
-               if(!(label & LABEL_TRANSPARENT)) {
-                       state->ray_pdf = bsdf_pdf;
-#ifdef __LAMP_MIS__
-                       state->ray_t = 0.0f;
-#endif
-                       state->min_ray_pdf = fminf(bsdf_pdf, state->min_ray_pdf);
-               }
-
-               /* update path state */
-               path_state_next(kg, state, label);
-
-               /* setup ray */
-               ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
-               ray->D = bsdf_omega_in;
-
-               if(state->bounce == 0)
-                       ray->t -= sd->ray_length; /* clipping works through transparent */
-               else
-                       ray->t = FLT_MAX;
-
-#ifdef __RAY_DIFFERENTIALS__
-               ray->dP = sd->dP;
-               ray->dD = bsdf_domega_in;
-#endif
-
-#ifdef __VOLUME__
-               /* enter/exit volume */
-               if(label & LABEL_TRANSMIT)
-                       kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
-#endif
-               return true;
-       }
-#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);
-
-               /* setup ray position, direction stays unchanged */
-               ray->P = ray_offset(sd->P, -sd->Ng);
-#ifdef __RAY_DIFFERENTIALS__
-               ray->dP = sd->dP;
-#endif
-
-               /* enter/exit volume */
-               kernel_volume_stack_enter_exit(kg, sd, state->volume_stack);
-               return true;
-       }
-#endif
-       else {
-               /* no bsdf or volume? */
-               return false;
        }
 }
 
@@ -677,8 +623,10 @@ ccl_device bool kernel_path_subsurface_scatter(KernelGlobals *kg, ShaderData *sd
 
                        hit_state.flag |= PATH_RAY_BSSRDF_ANCESTOR;
                        hit_state.rng_offset += PRNG_BOUNCE_NUM;
+                       
+                       kernel_path_surface_connect_light(kg, rng, &bssrdf_sd[hit], tp, state, L);
 
-                       if(kernel_path_integrate_lighting(kg, rng, &bssrdf_sd[hit], &tp, &hit_state, L, &hit_ray)) {
+                       if(kernel_path_surface_bounce(kg, rng, &bssrdf_sd[hit], &tp, &hit_state, L, &hit_ray)) {
 #ifdef __LAMP_MIS__
                                hit_state.ray_t = 0.0f;
 #endif
@@ -767,7 +715,9 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
                                &volume_sd, &volume_ray, &L, &throughput, rng);
 
                        if(result == VOLUME_PATH_SCATTERED) {
-                               if(kernel_path_integrate_scatter_lighting(kg, rng, &volume_sd, &throughput, &state, &L, &ray, 1.0f))
+                               kernel_path_volume_connect_light(kg, rng, &volume_sd, throughput, &state, &L, 1.0f);
+
+                               if(kernel_path_volume_bounce(kg, rng, &volume_sd, &throughput, &state, &L, &ray, 1.0f))
                                        continue;
                                else
                                        break;
@@ -876,115 +826,13 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
                                break;
                }
 #endif
-               
-               /* Same as kernel_path_integrate_lighting(kg, rng, &sd, &throughput, &state, &L, &ray),
-                  but for CUDA the function call is slower. */
-#ifdef __EMISSION__
-               if(kernel_data.integrator.use_direct_light) {
-                       /* sample illumination from lights to find path contribution */
-                       if(sd.flag & SD_BSDF_HAS_EVAL) {
-                               float light_t = path_state_rng_1D(kg, rng, &state, PRNG_LIGHT);
-                               float light_u, light_v;
-                               path_state_rng_2D(kg, rng, &state, PRNG_LIGHT_U, &light_u, &light_v);
-
-                               Ray light_ray;
-                               BsdfEval L_light;
-                               bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
-                               light_ray.time = sd.time;
-#endif
-
-                               LightSample ls;
-                               light_sample(kg, light_t, light_u, light_v, sd.time, sd.P, &ls);
-
-                               if(direct_emission(kg, &sd, &ls, &light_ray, &L_light, &is_lamp, state.bounce, state.transparent_bounce)) {
-                                       /* trace shadow ray */
-                                       float3 shadow;
-
-                                       if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
-                                               /* accumulate */
-                                               path_radiance_accum_light(&L, throughput, &L_light, shadow, 1.0f, state.bounce, is_lamp);
-                                       }
-                               }
-                       }
-               }
-#endif
-
-               if(sd.flag & SD_BSDF) {
-                       /* sample BSDF */
-                       float bsdf_pdf;
-                       BsdfEval bsdf_eval;
-                       float3 bsdf_omega_in;
-                       differential3 bsdf_domega_in;
-                       float bsdf_u, bsdf_v;
-                       path_state_rng_2D(kg, rng, &state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
-                       int label;
-
-                       label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
-                               &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
-                       if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
-                               break;
-
-                       /* modify throughput */
-                       path_radiance_bsdf_bounce(&L, &throughput, &bsdf_eval, bsdf_pdf, state.bounce, label);
-
-                       /* set labels */
-                       if(!(label & LABEL_TRANSPARENT)) {
-                               state.ray_pdf = bsdf_pdf;
-#ifdef __LAMP_MIS__
-                               state.ray_t = 0.0f;
-#endif
-                               state.min_ray_pdf = fminf(bsdf_pdf, state.min_ray_pdf);
-                       }
-
-                       /* update path state */
-                       path_state_next(kg, &state, label);
 
-                       /* setup ray */
-                       ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
-                       ray.D = bsdf_omega_in;
+               /* direct lighting */
+               kernel_path_surface_connect_light(kg, rng, &sd, throughput, &state, &L);
 
-#ifdef __RAY_DIFFERENTIALS__
-                       ray.dP = sd.dP;
-                       ray.dD = bsdf_domega_in;
-#endif
-
-#ifdef __VOLUME__
-                       /* enter/exit volume */
-                       if(label & LABEL_TRANSMIT)
-                               kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
-#endif
-
-               }
-#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);
-
-                       /* setup ray position, direction stays unchanged */
-                       ray.P = ray_offset(sd.P, -sd.Ng);
-#ifdef __RAY_DIFFERENTIALS__
-                       ray.dP = sd.dP;
-#endif
-
-                       /* enter/exit volume */
-                       kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack);
-               }
-#endif
-               else {
-                       /* no bsdf or volume? we're done */
+               /* compute direct lighting and next bounce */
+               if(!kernel_path_surface_bounce(kg, rng, &sd, &throughput, &state, &L, &ray))
                        break;
-               }
-
-               /* adjust ray distance for clipping */
-               if(state.bounce == 0)
-                       ray.t -= sd.ray_length; /* clipping works through transparent */
-               else
-                       ray.t = FLT_MAX;
        }
 
        float3 L_sum = path_radiance_clamp_and_sum(kg, &L);
@@ -996,17 +844,11 @@ ccl_device float4 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int sample,
 
 #ifdef __BRANCHED_PATH__
 
-ccl_device_noinline void kernel_branched_path_integrate_lighting(KernelGlobals *kg,
+/* branched path tracing: bounce off surface and integrate indirect light */
+ccl_device_noinline void kernel_branched_path_surface_indirect_light(KernelGlobals *kg,
        RNG *rng, ShaderData *sd, float3 throughput, float num_samples_adjust,
        PathState *state, PathRadiance *L)
 {
-#ifdef __EMISSION__
-       if(kernel_data.integrator.use_direct_light) {
-               bool all = kernel_data.integrator.sample_all_lights_direct;
-               kernel_branched_path_integrate_direct_lighting(kg, rng, sd, state, throughput, num_samples_adjust, L, all);
-       }
-#endif
-
        for(int i = 0; i< sd->num_closure; i++) {
                const ShaderClosure *sc = &sd->closure[i];
 
@@ -1127,15 +969,24 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg, Shade
 
                                path_state_branch(&hit_state, j, num_samples);
 
-                               kernel_branched_path_integrate_lighting(kg, rng,
-                                                                                                               &bssrdf_sd[hit], throughput, num_samples_inv,
-                                                                                                               &hit_state, L);
+#if defined(__EMISSION__) && defined(__BRANCHED_PATH__)
+                               /* direct light */
+                               if(kernel_data.integrator.use_direct_light) {
+                                       bool all = kernel_data.integrator.sample_all_lights_direct;
+                                       kernel_branched_path_surface_connect_light(kg, rng,
+                                               &bssrdf_sd[hit], &hit_state, throughput, num_samples_inv, L, all);
+                               }
+#endif
+
+                               /* indirect light */
+                               kernel_branched_path_surface_indirect_light(kg, rng,
+                                       &bssrdf_sd[hit], throughput, num_samples_inv,
+                                       &hit_state, L);
                        }
                }
 
                state->flag &= ~PATH_RAY_BSSRDF_ANCESTOR;
        }
-
 }
 #endif
 
@@ -1216,7 +1067,9 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
                                
                                if(result == VOLUME_PATH_SCATTERED) {
                                        /* todo: use all-light sampling */
-                                       if(kernel_path_integrate_scatter_lighting(kg, rng, &volume_sd, &tp, &ps, &L, &pray, num_samples_inv)) {
+                                       kernel_path_volume_connect_light(kg, rng, &volume_sd, tp, &state, &L, 1.0f);
+
+                                       if(kernel_path_volume_bounce(kg, rng, &volume_sd, &tp, &ps, &L, &pray, num_samples_inv)) {
                                                kernel_path_indirect(kg, rng, pray, tp*num_samples_inv, num_samples, ps, &L);
 
                                                /* for render passes, sum and reset indirect light pass variables
@@ -1367,8 +1220,17 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
                if(!(sd.flag & SD_HAS_ONLY_VOLUME)) {
                        PathState hit_state = state;
 
-                       /* lighting */
-                       kernel_branched_path_integrate_lighting(kg, rng,
+#ifdef __EMISSION__
+                       /* direct light */
+                       if(kernel_data.integrator.use_direct_light) {
+                               bool all = kernel_data.integrator.sample_all_lights_direct;
+                               kernel_branched_path_surface_connect_light(kg, rng,
+                                       &sd, &hit_state, throughput, 1.0f, &L, all);
+                       }
+#endif
+
+                       /* indirect light */
+                       kernel_branched_path_surface_indirect_light(kg, rng,
                                &sd, throughput, 1.0f, &hit_state, &L);
 
                        /* continue in case of transparency */