Cycles: better path termination for transparency.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 20 Feb 2018 13:22:40 +0000 (14:22 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Wed, 21 Feb 2018 23:55:32 +0000 (00:55 +0100)
We now continue transparent paths after diffuse/glossy/transmission/volume
bounces are exceeded. This avoids unexpected boundaries in volumes with
transparent boundaries. It is also required for MIS to work correctly with
transparent surfaces, as we also continue through these in shadow rays.

The main visible changes is that volumes will now be lit by the background
even at volume bounces 0, same as surfaces.

Fixes T53914 and T54103.

15 files changed:
intern/cycles/kernel/closure/bsdf_transparent.h
intern/cycles/kernel/kernel_bake.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_shader.h
intern/cycles/kernel/kernel_shadow.h
intern/cycles/kernel/kernel_subsurface.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/kernel_volume.h
intern/cycles/kernel/osl/osl_closures.cpp
intern/cycles/kernel/split/kernel_shader_eval.h
intern/cycles/kernel/svm/svm_closure.h
intern/cycles/render/osl.cpp

index 22ca7f3847e0b8f6ab6eb2a5fd3fc4d2a4edab05..79ee9dc4537c2ea996ad77dcf0b8df26b57aff3b 100644 (file)
 
 CCL_NAMESPACE_BEGIN
 
-ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight)
+ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int path_flag)
 {
        if(sd->flag & SD_TRANSPARENT) {
                sd->closure_transparent_extinction += weight;
+
+               for(int i = 0; i < sd->num_closure; i++) {
+                       ShaderClosure *sc = &sd->closure[i];
+
+                       if(sc->type == CLOSURE_BSDF_TRANSPARENT_ID) {
+                               sc->weight += weight;
+                               sc->sample_weight += fabsf(average(weight));
+                               break;
+                       }
+               }
        }
        else {
                sd->flag |= SD_BSDF|SD_TRANSPARENT;
                sd->closure_transparent_extinction = weight;
-       }
 
-       ShaderClosure *bsdf = bsdf_alloc(sd, sizeof(ShaderClosure), weight);
+               if(path_flag & PATH_RAY_TERMINATE) {
+                       /* In this case the number of closures is set to zero to disable
+                        * all others, but we still want to get transparency so increase
+                        * the number just for this. */
+                       sd->num_closure_left = 1;
+               }
+
+               ShaderClosure *bsdf = bsdf_alloc(sd, sizeof(ShaderClosure), weight);
 
-       if(bsdf) {
-               bsdf->N = sd->N;
-               bsdf->type = CLOSURE_BSDF_TRANSPARENT_ID;
+               if(bsdf) {
+                       bsdf->N = sd->N;
+                       bsdf->type = CLOSURE_BSDF_TRANSPARENT_ID;
+               }
        }
 }
 
index 8788e89c40e14bb753251a50245b421b760b1b89..b3c2450d10e13848ad28943c0da9bbdf5577dfeb 100644 (file)
@@ -51,7 +51,7 @@ ccl_device_inline void compute_light_pass(KernelGlobals *kg,
        path_state_init(kg, &emission_sd, &state, rng_hash, sample, NULL);
 
        /* evaluate surface shader */
-       shader_eval_surface(kg, sd, &state, state.flag, kernel_data.integrator.max_closures);
+       shader_eval_surface(kg, sd, &state, state.flag);
 
        /* TODO, disable more closures we don't need besides transparent */
        shader_bsdf_disable_transparency(kg, sd);
@@ -228,12 +228,12 @@ ccl_device float3 kernel_bake_evaluate_direct_indirect(KernelGlobals *kg,
                }
                else {
                        /* surface color of the pass only */
-                       shader_eval_surface(kg, sd, state, 0, kernel_data.integrator.max_closures);
+                       shader_eval_surface(kg, sd, state, 0);
                        return kernel_bake_shader_bsdf(kg, sd, type);
                }
        }
        else {
-               shader_eval_surface(kg, sd, state, 0, kernel_data.integrator.max_closures);
+               shader_eval_surface(kg, sd, state, 0);
                color = kernel_bake_shader_bsdf(kg, sd, type);
        }
 
@@ -333,7 +333,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
                {
                        float3 N = sd.N;
                        if((sd.flag & SD_HAS_BUMP)) {
-                               shader_eval_surface(kg, &sd, &state, 0, kernel_data.integrator.max_closures);
+                               shader_eval_surface(kg, &sd, &state, 0);
                                N = shader_bsdf_average_normal(kg, &sd);
                        }
 
@@ -348,7 +348,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input,
                }
                case SHADER_EVAL_EMISSION:
                {
-                       shader_eval_surface(kg, &sd, &state, 0, 0);
+                       shader_eval_surface(kg, &sd, &state, PATH_RAY_EMISSION);
                        out = shader_emissive_eval(kg, &sd);
                        break;
                }
index 94b0a37ce6201c0ea2f71df2330aa9bf093f840d..5875249b4049469451a8701bb6bdc9c7c0719de0 100644 (file)
@@ -67,13 +67,13 @@ ccl_device_noinline float3 direct_emissive_eval(KernelGlobals *kg,
 
                ls->Ng = emission_sd->Ng;
 
-               /* no path flag, we're evaluating this for all closures. that's weak but
-                * we'd have to do multiple evaluations otherwise */
+               /* No proper path flag, we're evaluating this for all closures. that's
+                * weak but we'd have to do multiple evaluations otherwise. */
                path_state_modify_bounce(state, true);
-               shader_eval_surface(kg, emission_sd, state, 0, 0);
+               shader_eval_surface(kg, emission_sd, state, PATH_RAY_EMISSION);
                path_state_modify_bounce(state, false);
 
-               /* evaluate emissive closure */
+               /* Evaluate emissive closure. */
                eval = shader_emissive_eval(kg, emission_sd);
        }
        
index 4728a25a3bc6a8bb5c044a1a34a39a050f8db478..aef350b065815e368cc010c1c270e1d9d77dc212 100644 (file)
@@ -446,11 +446,8 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg,
                }
 
                /* Setup and evaluate shader. */
-               shader_setup_from_ray(kg,
-                                     sd,
-                                     &isect,
-                                     ray);
-               shader_eval_surface(kg, sd, state, state->flag, kernel_data.integrator.max_closures);
+               shader_setup_from_ray(kg, sd, &isect, ray);
+               shader_eval_surface(kg, sd, state, state->flag);
                shader_prepare_closures(sd, state);
 
                /* Apply shadow catcher, holdout, emission. */
@@ -610,7 +607,7 @@ ccl_device_forceinline void kernel_path_integrate(
 
                /* Setup and evaluate shader. */
                shader_setup_from_ray(kg, &sd, &isect, ray);
-               shader_eval_surface(kg, &sd, state, state->flag, kernel_data.integrator.max_closures);
+               shader_eval_surface(kg, &sd, state, state->flag);
                shader_prepare_closures(&sd, state);
 
                /* Apply shadow catcher, holdout, emission. */
index 6fb55bda08dcb37c24b1ff1dbf51da93569f2bcf..441a06eeba33850ff7a3d1eb227a51d47b4556f4 100644 (file)
@@ -480,7 +480,7 @@ ccl_device void kernel_branched_path_integrate(KernelGlobals *kg,
 
                /* Setup and evaluate shader. */
                shader_setup_from_ray(kg, &sd, &isect, &ray);
-               shader_eval_surface(kg, &sd, &state, state.flag, kernel_data.integrator.max_closures);
+               shader_eval_surface(kg, &sd, &state, state.flag);
                shader_merge_closures(&sd);
 
                /* Apply shadow catcher, holdout, emission. */
index a16c20cbee6a7b9e3fb10fa6d380f7ca132d7e8f..15d81fcddf43d97a5140ceef5872637387296f47 100644 (file)
@@ -75,6 +75,9 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
        if(label & LABEL_TRANSPARENT) {
                state->flag |= PATH_RAY_TRANSPARENT;
                state->transparent_bounce++;
+               if(state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce) {
+                       state->flag |= PATH_RAY_TERMINATE_IMMEDIATE;
+               }
 
                if(!kernel_data.integrator.transparent_shadows)
                        state->flag |= PATH_RAY_MIS_SKIP;
@@ -86,6 +89,10 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
        }
 
        state->bounce++;
+       if(state->bounce >= kernel_data.integrator.max_bounce) {
+               state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+       }
+
        state->flag &= ~(PATH_RAY_ALL_VISIBILITY|PATH_RAY_MIS_SKIP);
 
 #ifdef __VOLUME__
@@ -95,6 +102,9 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
                state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND;
 
                state->volume_bounce++;
+               if(state->volume_bounce >= kernel_data.integrator.max_volume_bounce) {
+                       state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+               }
        }
        else
 #endif
@@ -104,10 +114,18 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
                        state->flag |= PATH_RAY_REFLECT;
                        state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND;
 
-                       if(label & LABEL_DIFFUSE)
+                       if(label & LABEL_DIFFUSE) {
                                state->diffuse_bounce++;
-                       else
+                               if(state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) {
+                                       state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+                               }
+                       }
+                       else {
                                state->glossy_bounce++;
+                               if(state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) {
+                                       state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+                               }
+                       }
                }
                else {
                        kernel_assert(label & LABEL_TRANSMIT);
@@ -119,6 +137,9 @@ ccl_device_inline void path_state_next(KernelGlobals *kg, ccl_addr_space PathSta
                        }
 
                        state->transmission_bounce++;
+                       if(state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce) {
+                               state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT;
+                       }
                }
 
                /* diffuse/glossy/singular */
@@ -162,13 +183,13 @@ ccl_device_inline float path_state_continuation_probability(KernelGlobals *kg,
                                                             ccl_addr_space PathState *state,
                                                             const float3 throughput)
 {
-       if(state->flag & PATH_RAY_TRANSPARENT) {
-               /* Transparent rays are treated separately with own max bounces. */
-               if(state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce) {
-                       return 0.0f;
-               }
+       if(state->flag & PATH_RAY_TERMINATE_IMMEDIATE) {
+               /* Ray is to be terminated immediately. */
+               return 0.0f;
+       }
+       else if(state->flag & PATH_RAY_TRANSPARENT) {
                /* Do at least one bounce without RR. */
-               else if(state->transparent_bounce <= 1) {
+               if(state->transparent_bounce <= 1) {
                        return 1.0f;
                }
 #ifdef __SHADOW_TRICKS__
@@ -179,19 +200,8 @@ ccl_device_inline float path_state_continuation_probability(KernelGlobals *kg,
 #endif
        }
        else {
-               /* Test max bounces for various ray types. */
-               if((state->bounce >= kernel_data.integrator.max_bounce) ||
-                  (state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) ||
-                  (state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) ||
-#ifdef __VOLUME__
-                  (state->volume_bounce >= kernel_data.integrator.max_volume_bounce) ||
-#endif
-                  (state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce))
-               {
-                       return 0.0f;
-               }
                /* Do at least one bounce without RR. */
-               else if(state->bounce <= 1) {
+               if(state->bounce <= 1) {
                        return 1.0f;
                }
 #ifdef __SHADOW_TRICKS__
index 5f2f00c5cebab8c42d76729491f5976ba81dd738..8cfd33b808ece340b5f04f59fb7fbdcf86c40e94 100644 (file)
@@ -966,10 +966,21 @@ ccl_device float3 shader_holdout_eval(KernelGlobals *kg, ShaderData *sd)
 /* Surface Evaluation */
 
 ccl_device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd,
-       ccl_addr_space PathState *state, int path_flag, int max_closure)
+       ccl_addr_space PathState *state, int path_flag)
 {
+       /* If path is being terminated, we are tracing a shadow ray or evaluating
+        * emission, then we don't need to store closures. The emission and shadow
+        * shader data also do not have a closure array to save GPU memory. */
+       int max_closures;
+       if(path_flag & (PATH_RAY_TERMINATE|PATH_RAY_SHADOW|PATH_RAY_EMISSION)) {
+               max_closures = 0;
+       }
+       else {
+               max_closures = kernel_data.integrator.max_closures;
+       }
+
        sd->num_closure = 0;
-       sd->num_closure_left = max_closure;
+       sd->num_closure_left = max_closures;
 
 #ifdef __OSL__
        if(kg->osl)
@@ -1140,13 +1151,23 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg,
                                           ShaderData *sd,
                                           ccl_addr_space PathState *state,
                                           ccl_addr_space VolumeStack *stack,
-                                          int path_flag,
-                                          int max_closure)
+                                          int path_flag)
 {
+       /* If path is being terminated, we are tracing a shadow ray or evaluating
+        * emission, then we don't need to store closures. The emission and shadow
+        * shader data also do not have a closure array to save GPU memory. */
+       int max_closures;
+       if(path_flag & (PATH_RAY_TERMINATE|PATH_RAY_SHADOW|PATH_RAY_EMISSION)) {
+               max_closures = 0;
+       }
+       else {
+               max_closures = kernel_data.integrator.max_closures;
+       }
+
        /* reset closures once at the start, we will be accumulating the closures
         * for all volumes in the stack into a single array of closures */
        sd->num_closure = 0;
-       sd->num_closure_left = max_closure;
+       sd->num_closure_left = max_closures;
        sd->flag = 0;
        sd->object_flag = 0;
 
index ab364d3037affe4dc102629b69f71e6aac888462..8a0da6c3b13ba6ee6207fe62afc462e0325b388c 100644 (file)
@@ -86,8 +86,7 @@ ccl_device_forceinline bool shadow_handle_transparent_isect(
                shader_eval_surface(kg,
                                    shadow_sd,
                                    state,
-                                   PATH_RAY_SHADOW,
-                                   0);
+                                   PATH_RAY_SHADOW);
                path_state_modify_bounce(state, false);
                *throughput *= shader_bsdf_transparency(kg, shadow_sd);
        }
index 134f362f5e6ca350ba523316ca66c5efedc7dad1..e8553d84547d615f443bcdb924ffbf44c032413e 100644 (file)
@@ -146,7 +146,7 @@ ccl_device void subsurface_color_bump_blur(KernelGlobals *kg,
 
        if(bump || texture_blur > 0.0f) {
                /* average color and normal at incoming point */
-               shader_eval_surface(kg, sd, state, state->flag, kernel_data.integrator.max_closures);
+               shader_eval_surface(kg, sd, state, state->flag);
                float3 in_color = shader_bssrdf_sum(sd, (bump)? N: NULL, NULL);
 
                /* we simply divide out the average color and multiply with the average
index c26769341f0401c84610c9294ea061d3c88fd264..44e7fd46adc87037608718902a55de609df30c89 100644 (file)
@@ -361,6 +361,17 @@ enum PathRayFlag {
        PATH_RAY_STORE_SHADOW_INFO           = (1 << 18),
        /* Zero background alpha, for camera or transparent glass rays. */
        PATH_RAY_TRANSPARENT_BACKGROUND      = (1 << 19),
+       /* Terminate ray immediately at next bounce. */
+       PATH_RAY_TERMINATE_IMMEDIATE         = (1 << 20),
+       /* Ray is to be terminated, but continue with transparent bounces and
+        * emission as long as we encounter them. This is required to make the
+        * MIS between direct and indirect light rays match, as shadow rays go
+        * through transparent surfaces to reach emisison too. */
+       PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1 << 21),
+       /* Ray is to be terminated. */
+       PATH_RAY_TERMINATE                   = (PATH_RAY_TERMINATE_IMMEDIATE|PATH_RAY_TERMINATE_AFTER_TRANSPARENT),
+       /* Path and shader is being evaluated for direct lighting emission. */
+       PATH_RAY_EMISSION                    = (1 << 22)
 };
 
 /* Closure Label */
index 7b67a37adc5b7c2b080d33ab82d662e51ca15171..3274e05f98e0b33b3ebc634bcaf13a37a777f2f6 100644 (file)
@@ -45,7 +45,7 @@ ccl_device_inline bool volume_shader_extinction_sample(KernelGlobals *kg,
                                                        float3 *extinction)
 {
        sd->P = P;
-       shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW, 0);
+       shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW);
 
        if(sd->flag & SD_EXTINCTION) {
                *extinction = sd->closure_transparent_extinction;
@@ -64,7 +64,7 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals *kg,
                                             VolumeShaderCoefficients *coeff)
 {
        sd->P = P;
-       shader_eval_volume(kg, sd, state, state->volume_stack, state->flag, kernel_data.integrator.max_closures);
+       shader_eval_volume(kg, sd, state, state->volume_stack, state->flag);
 
        if(!(sd->flag & (SD_EXTINCTION|SD_SCATTER|SD_EMISSION)))
                return false;
@@ -76,18 +76,11 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals *kg,
                                                    make_float3(0.0f, 0.0f, 0.0f);
 
        if(sd->flag & SD_SCATTER) {
-               if(state->bounce < kernel_data.integrator.max_bounce &&
-                  state->volume_bounce < kernel_data.integrator.max_volume_bounce) {
-                       for(int i = 0; i < sd->num_closure; i++) {
-                               const ShaderClosure *sc = &sd->closure[i];
+               for(int i = 0; i < sd->num_closure; i++) {
+                       const ShaderClosure *sc = &sd->closure[i];
 
-                               if(CLOSURE_IS_VOLUME(sc->type))
-                                       coeff->sigma_s += sc->weight;
-                       }
-               }
-               else {
-                       /* When at the max number of bounces, clear scattering. */
-                       sd->flag &= ~SD_SCATTER;
+                       if(CLOSURE_IS_VOLUME(sc->type))
+                               coeff->sigma_s += sc->weight;
                }
        }
 
index d0c357580fd3f77a8acf4d015b137e60c9e4cee2..ee16ddaf0fddf041deadb743dd597dc0b954ae27 100644 (file)
@@ -705,7 +705,7 @@ public:
 
        void setup(ShaderData *sd, int path_flag, float3 weight)
        {
-               bsdf_transparent_setup(sd, weight);
+               bsdf_transparent_setup(sd, weight, path_flag);
        }
 };
 
index 2409d1ba28b966f9a89bba1eabd643939d41af3f..2bc2d300699f5950cce3902ce97fcbb593f8aa33 100644 (file)
@@ -50,7 +50,7 @@ ccl_device void kernel_shader_eval(KernelGlobals *kg)
        if(IS_STATE(ray_state, ray_index, RAY_ACTIVE)) {
                ccl_global PathState *state = &kernel_split_state.path_state[ray_index];
 
-               shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, state->flag, kernel_data.integrator.max_closures);
+               shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, state->flag);
 #ifdef __BRANCHED_PATH__
                if(kernel_data.integrator.branched) {
                        shader_merge_closures(kernel_split_sd(sd, ray_index));
index 5398f36c26718104997fb4de997a5b1a078bac93..24452c81fe04883bc63afe7e2a17541d9318d541 100644 (file)
@@ -449,7 +449,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
                }
                case CLOSURE_BSDF_TRANSPARENT_ID: {
                        float3 weight = sd->svm_closure_weight * mix_weight;
-                       bsdf_transparent_setup(sd, weight);
+                       bsdf_transparent_setup(sd, weight, path_flag);
                        break;
                }
                case CLOSURE_BSDF_REFLECTION_ID:
@@ -728,7 +728,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
                                 * the throughput can blow up after multiple bounces. we
                                 * better figure out a way to skip backfaces from rays
                                 * spawned by transmission from the front */
-                               bsdf_transparent_setup(sd, make_float3(1.0f, 1.0f, 1.0f));
+                               bsdf_transparent_setup(sd, make_float3(1.0f, 1.0f, 1.0f), path_flag);
                        }
                        else {
                                HairBsdf *bsdf = (HairBsdf*)bsdf_alloc(sd, sizeof(HairBsdf), weight);
index 9c107274305c8da3fdaa5dbc243e2c8a96e0c59e..4d066c89b761df086a89308bbb146b0f8c6a7d37 100644 (file)
@@ -249,6 +249,9 @@ void OSLShaderManager::shading_system_init()
                        "__unused__",
                        "__unused__",
                        "__unused__",
+                       "__unused__",
+                       "__unused__",
+                       "__unused__",
                };
 
                const int nraytypes = sizeof(raytypes)/sizeof(raytypes[0]);