Cycles: First implementation of shadow catcher
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 9 Feb 2017 13:19:01 +0000 (14:19 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Mon, 27 Mar 2017 08:46:03 +0000 (10:46 +0200)
It uses an idea of accumulating all possible light reachable across the
light path (without taking shadow blocked into account) and accumulating
total shaded light across the path. Dividing second figure by first one
seems to be giving good estimate of the shadow.

In fact, to my knowledge, it's something really similar to what is
happening in the denoising branch, so we are aligned here which is good.

The workflow is following:

- Create an object which matches real-life object on which shadow is
  to be catched.

- Create approximate similar material on that object.

  This is needed to make indirect light properly affecting CG objects
  in the scene.

- Mark object as Shadow Catcher in the Object properties.

Ideally, after doing that it will be possible to render the image and
simply alpha-over it on top of real footage.

18 files changed:
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_object.cpp
intern/cycles/kernel/bvh/bvh.h
intern/cycles/kernel/bvh/bvh_shadow_all.h
intern/cycles/kernel/bvh/qbvh_shadow_all.h
intern/cycles/kernel/kernel_accumulate.h
intern/cycles/kernel/kernel_emission.h
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_shader.h
intern/cycles/kernel/kernel_shadow.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/split/kernel_indirect_background.h
intern/cycles/render/object.cpp
intern/cycles/render/object.h

index 6c5bcf0..cbf469b 100644 (file)
@@ -1096,6 +1096,12 @@ class CyclesObjectSettings(bpy.types.PropertyGroup):
                 default=1.0,
                 )
 
+        cls.is_shadow_catcher = BoolProperty(
+                name="Shadow Catcher",
+                description="Only render shadows on this object, for compositing renders into real footage",
+                default=False,
+                )
+
     @classmethod
     def unregister(cls):
         del bpy.types.Object.cycles
index c7b9b21..6136fc1 100644 (file)
@@ -797,6 +797,9 @@ class CyclesObject_PT_cycles_settings(CyclesButtonsPanel, Panel):
         sub.active = scene.render.use_simplify and cscene.use_distance_cull
         sub.prop(cob, "use_distance_cull")
 
+        col = layout.column()
+        col.prop(cob, "is_shadow_catcher")
+
 
 class CYCLES_OT_use_shading_nodes(Operator):
     """Enable nodes on a material, world or lamp"""
index 637cf7a..9688613 100644 (file)
@@ -343,6 +343,13 @@ Object *BlenderSync::sync_object(BL::Object& b_parent,
                object_updated = true;
        }
 
+       PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
+       bool is_shadow_catcher = get_boolean(cobject, "is_shadow_catcher");
+       if(is_shadow_catcher != object->is_shadow_catcher) {
+               object->is_shadow_catcher = is_shadow_catcher;
+               object_updated = true;
+       }
+
        /* object sync
         * transform comparison should not be needed, but duplis don't work perfect
         * in the depsgraph and may not signal changes, so this is a workaround */
index 598e138..1358288 100644 (file)
@@ -230,30 +230,63 @@ ccl_device_intersect void scene_intersect_subsurface(KernelGlobals *kg,
 #endif
 
 #ifdef __SHADOW_RECORD_ALL__
-ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, const Ray *ray, Intersection *isect, uint max_hits, uint *num_hits)
+ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg,
+                                                     const Ray *ray,
+                                                     Intersection *isect,
+                                                     int skip_object,
+                                                     uint max_hits,
+                                                     uint *num_hits)
 {
 #  ifdef __OBJECT_MOTION__
        if(kernel_data.bvh.have_motion) {
 #    ifdef __HAIR__
-               if(kernel_data.bvh.have_curves)
-                       return bvh_intersect_shadow_all_hair_motion(kg, ray, isect, max_hits, num_hits);
+               if(kernel_data.bvh.have_curves) {
+                       return bvh_intersect_shadow_all_hair_motion(kg,
+                                                                   ray,
+                                                                   isect,
+                                                                   skip_object,
+                                                                   max_hits,
+                                                                   num_hits);
+               }
 #    endif /* __HAIR__ */
 
-               return bvh_intersect_shadow_all_motion(kg, ray, isect, max_hits, num_hits);
+               return bvh_intersect_shadow_all_motion(kg,
+                                                      ray,
+                                                      isect,
+                                                      skip_object,
+                                                      max_hits,
+                                                      num_hits);
        }
 #  endif /* __OBJECT_MOTION__ */
 
 #  ifdef __HAIR__
-       if(kernel_data.bvh.have_curves)
-               return bvh_intersect_shadow_all_hair(kg, ray, isect, max_hits, num_hits);
+       if(kernel_data.bvh.have_curves) {
+               return bvh_intersect_shadow_all_hair(kg,
+                                                    ray,
+                                                    isect,
+                                                    skip_object,
+                                                    max_hits,
+                                                    num_hits);
+       }
 #  endif /* __HAIR__ */
 
 #  ifdef __INSTANCING__
-       if(kernel_data.bvh.have_instancing)
-               return bvh_intersect_shadow_all_instancing(kg, ray, isect, max_hits, num_hits);
+       if(kernel_data.bvh.have_instancing) {
+               return bvh_intersect_shadow_all_instancing(kg,
+                                                          ray,
+                                                          isect,
+                                                          skip_object,
+                                                          max_hits,
+                                                          num_hits);
+       }
 #  endif /* __INSTANCING__ */
 
-       return bvh_intersect_shadow_all(kg, ray, isect, max_hits, num_hits);
+       return bvh_intersect_shadow_all(kg,
+                                       ray,
+                                       isect,
+                                       skip_object,
+                                       max_hits,
+                                       num_hits);
 }
 #endif  /* __SHADOW_RECORD_ALL__ */
 
index 8f7c005..b2555b3 100644 (file)
@@ -45,6 +45,7 @@ ccl_device_inline
 bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
                                  const Ray *ray,
                                  Intersection *isect_array,
+                                 const int skip_object,
                                  const uint max_hits,
                                  uint *num_hits)
 {
@@ -189,6 +190,16 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
                                        while(prim_addr < prim_addr2) {
                                                kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type);
 
+#ifdef __SHADOW_TRICKS__
+                                               uint tri_object = (object == OBJECT_NONE)
+                                                       ? kernel_tex_fetch(__prim_object, prim_addr)
+                                                       : object;
+                                               if(tri_object == skip_object) {
+                                                       ++prim_addr;
+                                                       continue;
+                                               }
+#endif
+
                                                bool hit;
 
                                                /* todo: specialized intersect functions which don't fill in
@@ -398,6 +409,7 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
 ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg,
                                          const Ray *ray,
                                          Intersection *isect_array,
+                                         const int skip_object,
                                          const uint max_hits,
                                          uint *num_hits)
 {
@@ -406,6 +418,7 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg,
                return BVH_FUNCTION_FULL_NAME(QBVH)(kg,
                                                    ray,
                                                    isect_array,
+                                                   skip_object,
                                                    max_hits,
                                                    num_hits);
        }
@@ -416,6 +429,7 @@ ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg,
                return BVH_FUNCTION_FULL_NAME(BVH)(kg,
                                                   ray,
                                                   isect_array,
+                                                  skip_object,
                                                   max_hits,
                                                   num_hits);
        }
index 5d96078..1663e23 100644 (file)
@@ -33,6 +33,7 @@
 ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
                                              const Ray *ray,
                                              Intersection *isect_array,
+                                             const int skip_object,
                                              const uint max_hits,
                                              uint *num_hits)
 {
@@ -270,6 +271,16 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
                                        while(prim_addr < prim_addr2) {
                                                kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type);
 
+#ifdef __SHADOW_TRICKS__
+                                               uint tri_object = (object == OBJECT_NONE)
+                                                       ? kernel_tex_fetch(__prim_object, prim_addr)
+                                                       : object;
+                                               if(tri_object == skip_object) {
+                                                       ++prim_addr;
+                                                       continue;
+                                               }
+#endif
+
                                                bool hit;
 
                                                /* todo: specialized intersect functions which don't fill in
index c589c11..823d30d 100644 (file)
@@ -52,10 +52,17 @@ ccl_device_inline void bsdf_eval_init(BsdfEval *eval, ClosureType type, float3 v
        {
                eval->diffuse = value;
        }
+#ifdef __SHADOW_TRICKS__
+       eval->sum_no_mis = make_float3(0.0f, 0.0f, 0.0f);
+#endif
 }
 
-ccl_device_inline void bsdf_eval_accum(BsdfEval *eval, ClosureType type, float3 value)
+ccl_device_inline void bsdf_eval_accum(BsdfEval *eval, ClosureType type, float3 value, float mis_weight)
 {
+#ifdef __SHADOW_TRICKS__
+       eval->sum_no_mis += value;
+#endif
+       value *= mis_weight;
 #ifdef __PASSES__
        if(eval->use_light_pass) {
                if(CLOSURE_IS_BSDF_DIFFUSE(type))
@@ -96,7 +103,7 @@ ccl_device_inline bool bsdf_eval_is_zero(BsdfEval *eval)
        }
 }
 
-ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value)
+ccl_device_inline void bsdf_eval_mis(BsdfEval *eval, float value)
 {
 #ifdef __PASSES__
        if(eval->use_light_pass) {
@@ -115,8 +122,19 @@ ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value)
        }
 }
 
+ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value)
+{
+#ifdef __SHADOW_TRICKS__
+       eval->sum_no_mis *= value;
+#endif
+       bsdf_eval_mis(eval, value);
+}
+
 ccl_device_inline void bsdf_eval_mul3(BsdfEval *eval, float3 value)
 {
+#ifdef __SHADOW_TRICKS__
+       eval->sum_no_mis *= value;
+#endif
 #ifdef __PASSES__
        if(eval->use_light_pass) {
                eval->diffuse *= value;
@@ -134,7 +152,7 @@ ccl_device_inline void bsdf_eval_mul3(BsdfEval *eval, float3 value)
 #endif
 }
 
-ccl_device_inline float3 bsdf_eval_sum(BsdfEval *eval)
+ccl_device_inline float3 bsdf_eval_sum(const BsdfEval *eval)
 {
 #ifdef __PASSES__
        if(eval->use_light_pass) {
@@ -198,6 +216,12 @@ ccl_device_inline void path_radiance_init(PathRadiance *L, int use_light_pass)
        {
                L->emission = make_float3(0.0f, 0.0f, 0.0f);
        }
+
+#ifdef __SHADOW_TRICKS__
+       L->path_total = make_float3(0.0f, 0.0f, 0.0f);
+       L->path_total_shaded = make_float3(0.0f, 0.0f, 0.0f);
+       L->shadow_color = make_float3(0.0f, 0.0f, 0.0f);
+#endif
 }
 
 ccl_device_inline void path_radiance_bsdf_bounce(PathRadiance *L, ccl_addr_space float3 *throughput,
@@ -252,7 +276,12 @@ ccl_device_inline void path_radiance_accum_emission(PathRadiance *L, float3 thro
        }
 }
 
-ccl_device_inline void path_radiance_accum_ao(PathRadiance *L, float3 throughput, float3 alpha, float3 bsdf, float3 ao, int bounce)
+ccl_device_inline void path_radiance_accum_ao(PathRadiance *L,
+                                              float3 throughput,
+                                              float3 alpha,
+                                              float3 bsdf,
+                                              float3 ao,
+                                              int bounce)
 {
 #ifdef __PASSES__
        if(L->use_light_pass) {
@@ -271,6 +300,26 @@ ccl_device_inline void path_radiance_accum_ao(PathRadiance *L, float3 throughput
        {
                L->emission += throughput*bsdf*ao;
        }
+
+#ifdef __SHADOW_TRICKS__
+       float3 light = throughput * bsdf;
+       L->path_total += light;
+       L->path_total_shaded += ao * light;
+#endif
+}
+
+ccl_device_inline void path_radiance_accum_total_ao(
+        PathRadiance *L,
+        float3 throughput,
+        float3 bsdf)
+{
+#ifdef __SHADOW_TRICKS__
+       L->path_total += throughput * bsdf;
+#else
+       (void) L;
+       (void) throughput;
+       (void) bsdf;
+#endif
 }
 
 ccl_device_inline void path_radiance_accum_light(PathRadiance *L, float3 throughput, BsdfEval *bsdf_eval, float3 shadow, float shadow_fac, int bounce, bool is_lamp)
@@ -301,15 +350,38 @@ ccl_device_inline void path_radiance_accum_light(PathRadiance *L, float3 through
        {
                L->emission += throughput*bsdf_eval->diffuse*shadow;
        }
+
+#ifdef __SHADOW_TRICKS__
+       float3 light = throughput * bsdf_eval->sum_no_mis;
+       L->path_total += light;
+       L->path_total_shaded += shadow * light;
+#endif
+}
+
+ccl_device_inline void path_radiance_accum_total_light(
+        PathRadiance *L,
+        float3 throughput,
+        const BsdfEval *bsdf_eval)
+{
+#ifdef __SHADOW_TRICKS__
+       L->path_total += throughput * bsdf_eval->sum_no_mis;
+#else
+       (void) L;
+       (void) throughput;
+       (void) bsdf_eval;
+#endif
 }
 
-ccl_device_inline void path_radiance_accum_background(PathRadiance *L, float3 throughput, float3 value, int bounce)
+ccl_device_inline void path_radiance_accum_background(PathRadiance *L,
+                                                      ccl_addr_space PathState *state,
+                                                      float3 throughput,
+                                                      float3 value)
 {
 #ifdef __PASSES__
        if(L->use_light_pass) {
-               if(bounce == 0)
+               if(state->bounce == 0)
                        L->background += throughput*value;
-               else if(bounce == 1)
+               else if(state->bounce == 1)
                        L->direct_emission += throughput*value;
                else
                        L->indirect += throughput*value;
@@ -319,6 +391,13 @@ ccl_device_inline void path_radiance_accum_background(PathRadiance *L, float3 th
        {
                L->emission += throughput*value;
        }
+
+#ifdef __SHADOW_TRICKS__
+       L->path_total += throughput * value;
+       if(state->flag & PATH_RAY_SHADOW_CATCHER_ONLY) {
+               L->path_total_shaded += throughput * value;
+       }
+#endif
 }
 
 ccl_device_inline void path_radiance_sum_indirect(PathRadiance *L)
@@ -501,5 +580,34 @@ ccl_device_inline void path_radiance_accum_sample(PathRadiance *L, PathRadiance
        L->emission += L_sample->emission * fac;
 }
 
-CCL_NAMESPACE_END
+#ifdef __SHADOW_TRICKS__
+/* Calculate current shadow of the path. */
+ccl_device_inline float path_radiance_sum_shadow(const PathRadiance *L)
+{
+       float path_total = average(L->path_total);
+       float path_total_shaded = average(L->path_total_shaded);
+       if(path_total != 0.0f) {
+               return path_total_shaded / path_total;
+       }
+       return 1.0f;
+}
 
+/* Calculate final light sum and transparency for shadow catcher object. */
+ccl_device_inline float3 path_radiance_sum_shadowcatcher(KernelGlobals *kg,
+                                                         const PathRadiance *L,
+                                                         ccl_addr_space float* L_transparent)
+{
+       const float shadow = path_radiance_sum_shadow(L);
+       float3 L_sum;
+       if(kernel_data.background.transparent) {
+               *L_transparent = shadow;
+               L_sum = make_float3(0.0f, 0.0f, 0.0f);
+       }
+       else {
+               L_sum = L->shadow_color * shadow;
+       }
+       return L_sum;
+}
+#endif
+
+CCL_NAMESPACE_END
index cf14a15..9e7d51f 100644 (file)
@@ -156,7 +156,12 @@ ccl_device_noinline bool direct_emission(KernelGlobals *kg,
        if(bsdf_eval_is_zero(eval))
                return false;
 
-       if(kernel_data.integrator.light_inv_rr_threshold > 0.0f) {
+       if(kernel_data.integrator.light_inv_rr_threshold > 0.0f
+#ifdef __SHADOW_TRICKS__
+          && (state->flag & PATH_RAY_SHADOW_CATCHER) == 0
+#endif
+         )
+       {
                float probability = max3(fabs(bsdf_eval_sum(eval))) * kernel_data.integrator.light_inv_rr_threshold;
                if(probability < 1.0f) {
                        if(rand_terminate >= probability) {
index ebf03ad..74631aa 100644 (file)
@@ -92,6 +92,9 @@ ccl_device_noinline void kernel_path_ao(KernelGlobals *kg,
                if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow)) {
                        path_radiance_accum_ao(L, throughput, ao_alpha, ao_bsdf, ao_shadow, state->bounce);
                }
+               else {
+                       path_radiance_accum_total_ao(L, throughput, ao_bsdf);
+               }
        }
 }
 
@@ -290,9 +293,9 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
                        /* sample background shader */
                        float3 L_background = indirect_background(kg, emission_sd, state, ray);
                        path_radiance_accum_background(L,
+                                                      state,
                                                       throughput,
-                                                      L_background,
-                                                      state->bounce);
+                                                      L_background);
 #endif  /* __BACKGROUND__ */
 
                        break;
@@ -312,6 +315,12 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
                shader_merge_closures(sd);
 #endif  /* __BRANCHED_PATH__ */
 
+#ifdef __SHADOW_TRICKS__
+               if(!(sd->object_flag & SD_OBJECT_SHADOW_CATCHER)) {
+                       state->flag &= ~PATH_RAY_SHADOW_CATCHER_ONLY;
+               }
+#endif  /* __SHADOW_TRICKS__ */
+
                /* blurring of bsdf after bounces, for rays that have a small likelihood
                 * of following this particular path (diffuse, rough glossy) */
                if(kernel_data.integrator.filter_glossy != FLT_MAX) {
@@ -396,7 +405,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
 
 #if defined(__EMISSION__) && defined(__BRANCHED_PATH__)
                if(kernel_data.integrator.use_direct_light) {
-                       int all = kernel_data.integrator.sample_all_lights_indirect;
+                       int all = (kernel_data.integrator.sample_all_lights_indirect) ||
+                                 (state->flag & PATH_RAY_SHADOW_CATCHER);
                        kernel_branched_path_surface_connect_light(kg,
                                                                   rng,
                                                                   sd,
@@ -611,7 +621,7 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
 #ifdef __BACKGROUND__
                        /* sample background shader */
                        float3 L_background = indirect_background(kg, &emission_sd, &state, &ray);
-                       path_radiance_accum_background(&L, throughput, L_background, state.bounce);
+                       path_radiance_accum_background(&L, &state, throughput, L_background);
 #endif  /* __BACKGROUND__ */
 
                        break;
@@ -625,6 +635,21 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
                float rbsdf = path_state_rng_1D_for_decision(kg, rng, &state, PRNG_BSDF);
                shader_eval_surface(kg, &sd, rng, &state, rbsdf, state.flag, SHADER_CONTEXT_MAIN);
 
+#ifdef __SHADOW_TRICKS__
+               if((sd.object_flag & SD_OBJECT_SHADOW_CATCHER)) {
+                       if(state.flag & PATH_RAY_CAMERA) {
+                               state.flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_SHADOW_CATCHER_ONLY);
+                               state.catcher_object = sd.object;
+                               if(!kernel_data.background.transparent) {
+                                       L.shadow_color = indirect_background(kg, &emission_sd, &state, &ray);
+                               }
+                       }
+               }
+               else {
+                       state.flag &= ~PATH_RAY_SHADOW_CATCHER_ONLY;
+               }
+#endif  /* __SHADOW_TRICKS__ */
+
                /* holdout */
 #ifdef __HOLDOUT__
                if(((sd.flag & SD_HOLDOUT) ||
@@ -742,7 +767,16 @@ ccl_device_inline float4 kernel_path_integrate(KernelGlobals *kg,
        }
 #endif  /* __SUBSURFACE__ */
 
-       float3 L_sum = path_radiance_clamp_and_sum(kg, &L);
+       float3 L_sum;
+#ifdef __SHADOW_TRICKS__
+       if(state.flag & PATH_RAY_SHADOW_CATCHER) {
+               L_sum = path_radiance_sum_shadowcatcher(kg, &L, &L_transparent);
+       }
+       else
+#endif  /* __SHADOW_TRICKS__ */
+       {
+               L_sum = path_radiance_clamp_and_sum(kg, &L);
+       }
 
        kernel_write_light_passes(kg, buffer, &L, sample);
 
index d58960c..a2d4e34 100644 (file)
@@ -55,8 +55,12 @@ ccl_device_inline void kernel_branched_path_ao(KernelGlobals *kg,
                        light_ray.dP = sd->dP;
                        light_ray.dD = differential3_zero();
 
-                       if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow))
+                       if(!shadow_blocked(kg, emission_sd, state, &light_ray, &ao_shadow)) {
                                path_radiance_accum_ao(L, throughput*num_samples_inv, ao_alpha, ao_bsdf, ao_shadow, state->bounce);
+                       }
+                       else {
+                               path_radiance_accum_total_ao(L, throughput*num_samples_inv, ao_bsdf);
+                       }
                }
        }
 }
@@ -206,7 +210,8 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg,
 #ifdef __EMISSION__
                                /* direct light */
                                if(kernel_data.integrator.use_direct_light) {
-                                       int all = kernel_data.integrator.sample_all_lights_direct;
+                                       int all = (kernel_data.integrator.sample_all_lights_direct) ||
+                                                 (state->flag & PATH_RAY_SHADOW_CATCHER);
                                        kernel_branched_path_surface_connect_light(
                                                kg,
                                                rng,
@@ -461,7 +466,7 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
 #ifdef __BACKGROUND__
                        /* sample background shader */
                        float3 L_background = indirect_background(kg, &emission_sd, &state, &ray);
-                       path_radiance_accum_background(&L, throughput, L_background, state.bounce);
+                       path_radiance_accum_background(&L, &state, throughput, L_background);
 #endif  /* __BACKGROUND__ */
 
                        break;
@@ -472,6 +477,21 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
                shader_eval_surface(kg, &sd, rng, &state, 0.0f, state.flag, SHADER_CONTEXT_MAIN);
                shader_merge_closures(&sd);
 
+#ifdef __SHADOW_TRICKS__
+               if((sd.object_flag & SD_OBJECT_SHADOW_CATCHER)) {
+                       if(state.flag & PATH_RAY_CAMERA) {
+                               state.flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_SHADOW_CATCHER_ONLY);
+                               state.catcher_object = sd.object;
+                               if(!kernel_data.background.transparent) {
+                                       L.shadow_color = indirect_background(kg, &emission_sd, &state, &ray);
+                               }
+                       }
+               }
+               else {
+                       state.flag &= ~PATH_RAY_SHADOW_CATCHER_ONLY;
+               }
+#endif  /* __SHADOW_TRICKS__ */
+
                /* holdout */
 #ifdef __HOLDOUT__
                if((sd.flag & SD_HOLDOUT) || (sd.object_flag & SD_OBJECT_HOLDOUT_MASK)) {
@@ -544,7 +564,8 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
 #ifdef __EMISSION__
                        /* direct light */
                        if(kernel_data.integrator.use_direct_light) {
-                               int all = kernel_data.integrator.sample_all_lights_direct;
+                               int all = (kernel_data.integrator.sample_all_lights_direct) ||
+                                         (state.flag & PATH_RAY_SHADOW_CATCHER);
                                kernel_branched_path_surface_connect_light(kg, rng,
                                        &sd, &emission_sd, &hit_state, throughput, 1.0f, &L, all);
                        }
@@ -581,7 +602,16 @@ ccl_device float4 kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, in
 #endif  /* __VOLUME__ */
        }
 
-       float3 L_sum = path_radiance_clamp_and_sum(kg, &L);
+       float3 L_sum;
+#ifdef __SHADOW_TRICKS__
+       if(state.flag & PATH_RAY_SHADOW_CATCHER) {
+               L_sum = path_radiance_sum_shadowcatcher(kg, &L, &L_transparent);
+       }
+       else
+#endif  /* __SHADOW_TRICKS__ */
+       {
+               L_sum = path_radiance_clamp_and_sum(kg, &L);
+       }
 
        kernel_write_light_passes(kg, buffer, &L, sample);
 
index 661dc52..e85f7dc 100644 (file)
@@ -54,6 +54,10 @@ ccl_device_inline void path_state_init(KernelGlobals *kg,
                state->volume_stack[0].shader = SHADER_NONE;
        }
 #endif
+
+#ifdef __SHADOW_TRICKS__
+       state->catcher_object = OBJECT_NONE;
+#endif
 }
 
 ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathState *state, int label)
index efa2303..d6bfe9b 100644 (file)
@@ -16,8 +16,7 @@
 
 CCL_NAMESPACE_BEGIN
 
-#if (defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__)) && !defined(__SPLIT_KERNEL__)
-
+#if (defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__) || defined(__SHADOW_TRICKS__)) && !defined(__SPLIT_KERNEL__)
 /* branched path tracing: connect path directly to position on one or more lights and add it to L */
 ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobals *kg, RNG *rng,
        ShaderData *sd, ShaderData *emission_sd, PathState *state, float3 throughput,
@@ -66,6 +65,9 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
                                                        /* accumulate */
                                                        path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
                                                }
+                                               else {
+                                                       path_radiance_accum_total_light(L, throughput*num_samples_inv, &L_light);
+                                               }
                                        }
                                }
                        }
@@ -100,6 +102,9 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
                                                        /* accumulate */
                                                        path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state->bounce, is_lamp);
                                                }
+                                               else {
+                                                       path_radiance_accum_total_light(L, throughput*num_samples_inv, &L_light);
+                                               }
                                        }
                                }
                        }
@@ -123,6 +128,9 @@ ccl_device_noinline void kernel_branched_path_surface_connect_light(KernelGlobal
                                        /* accumulate */
                                        path_radiance_accum_light(L, throughput*num_samples_adjust, &L_light, shadow, num_samples_adjust, state->bounce, is_lamp);
                                }
+                               else {
+                                       path_radiance_accum_total_light(L, throughput*num_samples_adjust, &L_light);
+                               }
                        }
                }
        }
@@ -197,6 +205,21 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, ccl_
        if(!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)))
                return;
 
+#ifdef __SHADOW_TRICKS__
+       if(state->flag & PATH_RAY_SHADOW_CATCHER) {
+               kernel_branched_path_surface_connect_light(kg,
+                                                          rng,
+                                                          sd,
+                                                          emission_sd,
+                                                          state,
+                                                          throughput,
+                                                          1.0f,
+                                                          L,
+                                                          1);
+               return;
+       }
+#endif
+
        /* 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;
@@ -221,6 +244,9 @@ ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, ccl_
                                /* accumulate */
                                path_radiance_accum_light(L, throughput, &L_light, shadow, 1.0f, state->bounce, is_lamp);
                        }
+                       else {
+                               path_radiance_accum_total_light(L, throughput, &L_light);
+                       }
                }
        }
 #endif
index 93a92c6..5d942de 100644 (file)
@@ -516,7 +516,7 @@ ccl_device_inline void _shader_bsdf_multi_eval(KernelGlobals *kg, ShaderData *sd
                        float3 eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf);
 
                        if(bsdf_pdf != 0.0f) {
-                               bsdf_eval_accum(result_eval, sc->type, eval*sc->weight);
+                               bsdf_eval_accum(result_eval, sc->type, eval*sc->weight, 1.0f);
                                sum_pdf += bsdf_pdf*sc->sample_weight;
                        }
 
@@ -544,7 +544,8 @@ ccl_device_inline void _shader_bsdf_multi_eval_branched(KernelGlobals *kg,
                                float mis_weight = use_mis? power_heuristic(light_pdf, bsdf_pdf): 1.0f;
                                bsdf_eval_accum(result_eval,
                                                sc->type,
-                                               eval * sc->weight * mis_weight);
+                                               eval * sc->weight,
+                                               mis_weight);
                        }
                }
        }
@@ -576,7 +577,7 @@ void shader_bsdf_eval(KernelGlobals *kg,
                _shader_bsdf_multi_eval(kg, sd, omega_in, &pdf, -1, eval, 0.0f, 0.0f);
                if(use_mis) {
                        float weight = power_heuristic(light_pdf, pdf);
-                       bsdf_eval_mul(eval, weight);
+                       bsdf_eval_mis(eval, weight);
                }
        }
 }
@@ -944,7 +945,7 @@ ccl_device_inline void _shader_volume_phase_multi_eval(const ShaderData *sd, con
                        float3 eval = volume_phase_eval(sd, sc, omega_in, &phase_pdf);
 
                        if(phase_pdf != 0.0f) {
-                               bsdf_eval_accum(result_eval, sc->type, eval);
+                               bsdf_eval_accum(result_eval, sc->type, eval, 1.0f);
                                sum_pdf += phase_pdf*sc->sample_weight;
                        }
 
index 4efc6c8..0426e0a 100644 (file)
@@ -128,6 +128,7 @@ ccl_device bool shadow_blocked_opaque(KernelGlobals *kg,
 ccl_device bool shadow_blocked_transparent_all_loop(KernelGlobals *kg,
                                                     ShaderData *shadow_sd,
                                                     ccl_addr_space PathState *state,
+                                                    const int skip_object,
                                                     Ray *ray,
                                                     Intersection *hits,
                                                     uint max_hits,
@@ -140,6 +141,7 @@ ccl_device bool shadow_blocked_transparent_all_loop(KernelGlobals *kg,
        const bool blocked = scene_intersect_shadow_all(kg,
                                                        ray,
                                                        hits,
+                                                       skip_object,
                                                        max_hits,
                                                        &num_hits);
        /* If no opaque surface found but we did find transparent hits,
@@ -216,6 +218,7 @@ ccl_device bool shadow_blocked_transparent_all_loop(KernelGlobals *kg,
 ccl_device bool shadow_blocked_transparent_all(KernelGlobals *kg,
                                                ShaderData *shadow_sd,
                                                ccl_addr_space PathState *state,
+                                               const int skip_object,
                                                Ray *ray,
                                                uint max_hits,
                                                float3 *shadow)
@@ -250,6 +253,7 @@ ccl_device bool shadow_blocked_transparent_all(KernelGlobals *kg,
        return shadow_blocked_transparent_all_loop(kg,
                                                   shadow_sd,
                                                   state,
+                                                  skip_object,
                                                   ray,
                                                   hits,
                                                   max_hits,
@@ -274,13 +278,14 @@ ccl_device bool shadow_blocked_transparent_stepped_loop(
         KernelGlobals *kg,
         ShaderData *shadow_sd,
         ccl_addr_space PathState *state,
+        const int skip_object,
         Ray *ray,
         Intersection *isect,
         const bool blocked,
         const bool is_transparent_isect,
         float3 *shadow)
 {
-       if(blocked && is_transparent_isect) {
+       if((blocked && is_transparent_isect) || skip_object != OBJECT_NONE) {
                float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
                float3 Pend = ray->P + ray->D*ray->t;
                int bounce = state->transparent_bounce;
@@ -306,6 +311,23 @@ ccl_device bool shadow_blocked_transparent_stepped_loop(
                        {
                                break;
                        }
+#ifdef __SHADOW_TRICKS__
+                       if(skip_object != OBJECT_NONE) {
+                               const int isect_object = (isect->object == PRIM_NONE)
+                                       ? kernel_tex_fetch(__prim_object, isect->prim)
+                                       : isect->object;
+                               if(isect_object == skip_object) {
+                                       shader_setup_from_ray(kg, shadow_sd, isect, ray);
+                                       /* Move ray forward. */
+                                       ray->P = ray_offset(shadow_sd->P, -shadow_sd->Ng);
+                                       if(ray->t != FLT_MAX) {
+                                               ray->D = normalize_len(Pend - ray->P, &ray->t);
+                                       }
+                                       bounce++;
+                                       continue;
+                               }
+                       }
+#endif
                        if(!shader_transparent_shadow(kg, isect)) {
                                return true;
                        }
@@ -351,22 +373,31 @@ ccl_device bool shadow_blocked_transparent_stepped(
         KernelGlobals *kg,
         ShaderData *shadow_sd,
         ccl_addr_space PathState *state,
+        const int skip_object,
         Ray *ray,
         Intersection *isect,
         float3 *shadow)
 {
-       const bool blocked = scene_intersect(kg,
-                                            *ray,
-                                            PATH_RAY_SHADOW_OPAQUE,
-                                            isect,
-                                            NULL,
-                                            0.0f, 0.0f);
-       const bool is_transparent_isect = blocked
-               ? shader_transparent_shadow(kg, isect)
-               : false;
+       bool blocked, is_transparent_isect;
+       if (skip_object == OBJECT_NONE) {
+               blocked = scene_intersect(kg,
+                                         *ray,
+                                         PATH_RAY_SHADOW_OPAQUE,
+                                         isect,
+                                         NULL,
+                                         0.0f, 0.0f);
+               is_transparent_isect = blocked
+                               ? shader_transparent_shadow(kg, isect)
+                               : false;
+       }
+       else {
+               blocked = false;
+               is_transparent_isect = false;
+       }
        return shadow_blocked_transparent_stepped_loop(kg,
                                                       shadow_sd,
                                                       state,
+                                                      skip_object,
                                                       ray,
                                                       isect,
                                                       blocked,
@@ -390,12 +421,21 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg,
        if(ray->t == 0.0f) {
                return false;
        }
+#ifdef __SHADOW_TRICKS__
+    const int skip_object = state->catcher_object;
+#else
+    const int skip_object = OBJECT_NONE;
+#endif
        /* Do actual shadow shading. */
        /* First of all, we check if integrator requires transparent shadows.
         * if not, we use simplest and fastest ever way to calculate occlusion.
+        *
+        * NOTE: We can't do quick opaque test here if we are on shadow-catcher
+        * path because we don't want catcher object to be casting shadow here.
         */
 #ifdef __TRANSPARENT_SHADOWS__
-       if(!kernel_data.integrator.transparent_shadows)
+       if(!kernel_data.integrator.transparent_shadows &&
+          skip_object == OBJECT_NONE)
 #endif
        {
                return shadow_blocked_opaque(kg,
@@ -440,6 +480,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg,
                return shadow_blocked_transparent_stepped_loop(kg,
                                                               shadow_sd,
                                                               state,
+                                                              skip_object,
                                                               ray,
                                                               &isect,
                                                               blocked,
@@ -450,6 +491,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg,
        return shadow_blocked_transparent_all(kg,
                                              shadow_sd,
                                              state,
+                                             skip_object,
                                              ray,
                                              max_hits,
                                              shadow);
@@ -458,6 +500,7 @@ ccl_device_inline bool shadow_blocked(KernelGlobals *kg,
        return shadow_blocked_transparent_stepped(kg,
                                                  shadow_sd,
                                                  state,
+                                                 skip_object,
                                                  ray,
                                                  &isect,
                                                  shadow);
index 245832a..60bb7b7 100644 (file)
@@ -158,6 +158,10 @@ CCL_NAMESPACE_BEGIN
 #define __CLAMP_SAMPLE__
 #define __PATCH_EVAL__
 
+#ifndef __SPLIT_KERNEL__
+#  define __SHADOW_TRICKS__
+#endif
+
 #ifdef __KERNEL_SHADING__
 #  define __SVM__
 #  define __EMISSION__
@@ -316,6 +320,8 @@ enum PathRayFlag {
        PATH_RAY_MIS_SKIP = 4096,
        PATH_RAY_DIFFUSE_ANCESTOR = 8192,
        PATH_RAY_SINGLE_PASS_DONE = 16384,
+       PATH_RAY_SHADOW_CATCHER = 32768,
+       PATH_RAY_SHADOW_CATCHER_ONLY = 65536,
 };
 
 /* Closure Label */
@@ -445,6 +451,20 @@ typedef ccl_addr_space struct PathRadiance {
        float4 shadow;
        float mist;
 #endif
+
+#ifdef __SHADOW_TRICKS__
+       /* Total light reachable across the path, ignoring shadow blocked queries. */
+       float3 path_total;
+       /* Total light reachable across the path with shadow blocked queries
+        * applied here.
+        *
+        * Dividing this figure by path_total will give estimate of shadow pass.
+        */
+       float3 path_total_shaded;
+
+       /* Color of the background on which shadow is alpha-overed. */
+       float3 shadow_color;
+#endif
 } PathRadiance;
 
 typedef struct BsdfEval {
@@ -460,6 +480,9 @@ typedef struct BsdfEval {
        float3 subsurface;
        float3 scatter;
 #endif
+#ifdef __SHADOW_TRICKS__
+       float3 sum_no_mis;
+#endif
 } BsdfEval;
 
 /* Shader Flag */
@@ -805,13 +828,16 @@ enum ShaderDataObjectFlag {
        SD_OBJECT_INTERSECTS_VOLUME      = (1 << 5),
        /* Has position for motion vertices. */
        SD_OBJECT_HAS_VERTEX_MOTION      = (1 << 6),
+       /* object is used to catch shadows */
+       SD_OBJECT_SHADOW_CATCHER         = (1 << 7),
 
        SD_OBJECT_FLAGS = (SD_OBJECT_HOLDOUT_MASK |
                           SD_OBJECT_MOTION |
                           SD_OBJECT_TRANSFORM_APPLIED |
                           SD_OBJECT_NEGATIVE_SCALE_APPLIED |
                           SD_OBJECT_HAS_VOLUME |
-                          SD_OBJECT_INTERSECTS_VOLUME)
+                          SD_OBJECT_INTERSECTS_VOLUME |
+                          SD_OBJECT_SHADOW_CATCHER)
 };
 
 typedef ccl_addr_space struct ShaderData {
@@ -930,6 +956,10 @@ typedef struct PathState {
        RNG rng_congruential;
        VolumeStack volume_stack[VOLUME_STACK_SIZE];
 #endif
+
+#ifdef __SHADOW_TRICKS__
+       int catcher_object;
+#endif
 } PathState;
 
 /* Subsurface */
index 100f599..8192528 100644 (file)
@@ -70,7 +70,7 @@ ccl_device void kernel_indirect_background(KernelGlobals *kg)
 #ifdef __BACKGROUND__
                        /* sample background shader */
                        float3 L_background = indirect_background(kg, &kernel_split_state.sd_DL_shadow[ray_index], state, ray);
-                       path_radiance_accum_background(L, (*throughput), L_background, state->bounce);
+                       path_radiance_accum_background(L, state, (*throughput), L_background);
 #endif
                        ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER);
                }
index 8342f37..a3a8c6d 100644 (file)
@@ -49,6 +49,8 @@ NODE_DEFINE(Object)
        SOCKET_POINT(dupli_generated, "Dupli Generated", make_float3(0.0f, 0.0f, 0.0f));
        SOCKET_POINT2(dupli_uv, "Dupli UV", make_float2(0.0f, 0.0f));
 
+       SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", false);
+
        return type;
 }
 
@@ -597,6 +599,12 @@ void ObjectManager::device_update_flags(Device *device,
                else {
                        object_flag[object_index] &= ~SD_OBJECT_HAS_VOLUME;
                }
+               if(object->is_shadow_catcher) {
+                       object_flag[object_index] |= SD_OBJECT_SHADOW_CATCHER;
+               }
+               else {
+                       object_flag[object_index] &= ~SD_OBJECT_SHADOW_CATCHER;
+               }
 
                if(bounds_valid) {
                        foreach(Object *volume_object, volume_objects) {
index 3495849..dabe0f7 100644 (file)
@@ -53,6 +53,7 @@ public:
        bool use_motion;
        bool hide_on_missing_motion;
        bool use_holdout;
+       bool is_shadow_catcher;
 
        float3 dupli_generated;
        float2 dupli_uv;