Cycles: random walk subsurface scattering.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Sun, 21 Jan 2018 13:04:22 +0000 (14:04 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Fri, 9 Feb 2018 18:58:33 +0000 (19:58 +0100)
It is basically brute force volume scattering within the mesh, but part
of the SSS code for faster performance. The main difference with actual
volume scattering is that we assume the boundaries are diffuse and that
all lighting is coming through this boundary from outside the volume.

This gives much more accurate results for thin features and low density.
Some challenges remain however:

* Significantly more noisy than BSSRDF. Adding Dwivedi sampling may help
  here, but it's unclear still how much it helps in real world cases.
* Due to this being a volumetric method, geometry like eyes or mouth can
  darken the skin on the outside. We may be able to reduce this effect,
  or users can compensate for it by reducing the scattering radius in
  such areas.
* Sharp corners are quite bright. This matches actual volume rendering
  and results in some other renderers, but maybe not so much real world
  objects.

Differential Revision: https://developer.blender.org/D3054

22 files changed:
intern/cycles/blender/blender_shader.cpp
intern/cycles/kernel/closure/bsdf.h
intern/cycles/kernel/closure/bssrdf.h
intern/cycles/kernel/closure/volume.h
intern/cycles/kernel/geom/geom_motion_triangle_intersect.h
intern/cycles/kernel/geom/geom_triangle_intersect.h
intern/cycles/kernel/kernel_path.h
intern/cycles/kernel/kernel_path_branched.h
intern/cycles/kernel/kernel_path_subsurface.h
intern/cycles/kernel/kernel_subsurface.h
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/kernel_volume.h
intern/cycles/kernel/osl/osl_bssrdf.cpp
intern/cycles/kernel/shaders/node_subsurface_scattering.osl
intern/cycles/kernel/split/kernel_subsurface_scatter.h
intern/cycles/kernel/svm/svm_closure.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/integrator.cpp
intern/cycles/render/nodes.cpp
intern/cycles/util/util_math_float3.h
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/intern/rna_nodetree.c

index ac1eba85dbb737fe6231696cf53333178af96f0f..d6f7a08431d32937de513c3f17b3b408a74ef1c8 100644 (file)
@@ -433,6 +433,9 @@ static ShaderNode *add_node(Scene *scene,
                        case BL::ShaderNodeSubsurfaceScattering::falloff_BURLEY:
                                subsurface->falloff = CLOSURE_BSSRDF_BURLEY_ID;
                                break;
+                       case BL::ShaderNodeSubsurfaceScattering::falloff_RANDOM_WALK:
+                               subsurface->falloff = CLOSURE_BSSRDF_RANDOM_WALK_ID;
+                               break;
                }
 
                node = subsurface;
index 6f0bdb3fa38214338c8d330b3dfed419bc26c379..e3beff6675a9ce77852156ac3a80d1daf4e7bc05 100644 (file)
@@ -30,9 +30,7 @@
 #include "kernel/closure/bsdf_principled_diffuse.h"
 #include "kernel/closure/bsdf_principled_sheen.h"
 #include "kernel/closure/bssrdf.h"
-#ifdef __VOLUME__
-#  include "kernel/closure/volume.h"
-#endif
+#include "kernel/closure/volume.h"
 
 CCL_NAMESPACE_BEGIN
 
index c8f505e84180d304e94b57b314fa0e219a244059..790368ee888b0cb3e330c07575b944691054881e 100644 (file)
@@ -408,7 +408,8 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type)
                bssrdf->sharpness = saturate(bssrdf->sharpness);
 
                if(type == CLOSURE_BSSRDF_BURLEY_ID ||
-                  type == CLOSURE_BSSRDF_PRINCIPLED_ID)
+                  type == CLOSURE_BSSRDF_PRINCIPLED_ID ||
+                  type == CLOSURE_BSSRDF_RANDOM_WALK_ID)
                {
                        bssrdf_burley_setup(bssrdf);
                }
index 4bb5e680723253f61f12a6c7c4ce5f4917d8b751..da791e9aa734d518303ee5a9c325f2d254fcbb4a 100644 (file)
@@ -83,35 +83,45 @@ ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, c
        return make_float3(*pdf, *pdf, *pdf);
 }
 
-ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, float3 I, float3 dIdx, float3 dIdy, float randu, float randv,
-       float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
+ccl_device float3 henyey_greenstrein_sample(float3 D, float g, float randu, float randv, float *pdf)
 {
-       const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume*)sc;
-       float g = volume->g;
-       float cos_phi, sin_phi, cos_theta;
-
        /* match pdf for small g */
-       if(fabsf(g) < 1e-3f) {
+       float cos_theta;
+       bool isotropic = fabsf(g) < 1e-3f;
+
+       if(isotropic) {
                cos_theta = (1.0f - 2.0f * randu);
-               *pdf = M_1_PI_F * 0.25f;
+               if(pdf) {
+                       *pdf = M_1_PI_F * 0.25f;
+               }
        }
        else {
                float k = (1.0f - g * g) / (1.0f - g + 2.0f * g * randu);
                cos_theta = (1.0f + g * g - k * k) / (2.0f * g);
-               *pdf = single_peaked_henyey_greenstein(cos_theta, g);
+               if(pdf) {
+                       *pdf = single_peaked_henyey_greenstein(cos_theta, g);
+               }
        }
 
        float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta);
-
        float phi = M_2PI_F * randv;
-       cos_phi = cosf(phi);
-       sin_phi = sinf(phi);
+       float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta);
 
-       /* note that I points towards the viewer and so is used negated */
        float3 T, B;
-       make_orthonormals(-I, &T, &B);
-       *omega_in = sin_theta * cos_phi * T + sin_theta * sin_phi * B + cos_theta * (-I);
+       make_orthonormals(D, &T, &B);
+       dir = dir.x * T + dir.y * B + dir.z * D;
+
+       return dir;
+}
+
+ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, float3 I, float3 dIdx, float3 dIdy, float randu, float randv,
+       float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
+{
+       const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume*)sc;
+       float g = volume->g;
 
+       /* note that I points towards the viewer and so is used negated */
+       *omega_in = henyey_greenstrein_sample(-I, g, randu, randv, pdf);
        *eval = make_float3(*pdf, *pdf, *pdf); /* perfect importance sampling */
 
 #ifdef __RAY_DIFFERENTIALS__
index 4a19f2ba0314abe941afa03d69b7ffee0a4f7d55..542843edc8463369802d46d175183332f01ffbb2 100644 (file)
@@ -248,23 +248,30 @@ ccl_device_inline void motion_triangle_intersect_local(
        motion_triangle_vertices(kg, local_object, prim, time, verts);
        /* Ray-triangle intersection, unoptimized. */
        float t, u, v;
-       if(ray_triangle_intersect(P,
-                                 dir,
-                                 tmax,
+       if(!ray_triangle_intersect(P,
+                                  dir,
+                                  tmax,
 #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__)
-                                 (ssef*)verts,
+                                  (ssef*)verts,
 #else
-                                 verts[0], verts[1], verts[2],
+                                  verts[0], verts[1], verts[2],
 #endif
-                                 &u, &v, &t))
+                                  &u, &v, &t))
        {
+               return;
+       }
+
+       int hit;
+       if(lcg_state) {
+               /* Record up to max_hits intersections. */
                for(int i = min(max_hits, local_isect->num_hits) - 1; i >= 0; --i) {
                        if(local_isect->hits[i].t == t) {
                                return;
                        }
                }
+
                local_isect->num_hits++;
-               int hit;
+
                if(local_isect->num_hits <= max_hits) {
                        hit = local_isect->num_hits - 1;
                }
@@ -277,18 +284,29 @@ ccl_device_inline void motion_triangle_intersect_local(
                        if(hit >= max_hits)
                                return;
                }
-               /* Record intersection. */
-               Intersection *isect = &local_isect->hits[hit];
-               isect->t = t;
-               isect->u = u;
-               isect->v = v;
-               isect->prim = prim_addr;
-               isect->object = object;
-               isect->type = PRIMITIVE_MOTION_TRIANGLE;
-               /* Record geometric normal. */
-               local_isect->Ng[hit] = normalize(cross(verts[1] - verts[0],
-                                                   verts[2] - verts[0]));
        }
+       else {
+               /* Record closest intersection only. */
+               if(local_isect->num_hits && t > local_isect->hits[0].t) {
+                       return;
+               }
+
+               hit = 0;
+               local_isect->num_hits = 1;
+       }
+
+       /* Record intersection. */
+       Intersection *isect = &local_isect->hits[hit];
+       isect->t = t;
+       isect->u = u;
+       isect->v = v;
+       isect->prim = prim_addr;
+       isect->object = object;
+       isect->type = PRIMITIVE_MOTION_TRIANGLE;
+
+       /* Record geometric normal. */
+       local_isect->Ng[hit] = normalize(cross(verts[1] - verts[0],
+                                              verts[2] - verts[0]));
 }
 #endif  /* __BVH_LOCAL__ */
 
index 7daa378f81e5abfb1913b77d959ec98417605f75..a3b23115ae433012c3519ce4ebfbd62b8e33d5f9 100644 (file)
@@ -118,28 +118,40 @@ ccl_device_inline void triangle_intersect_local(
                return;
        }
 
-       for(int i = min(max_hits, local_isect->num_hits) - 1; i >= 0; --i) {
-               if(local_isect->hits[i].t == t) {
-                       return;
+       int hit;
+       if(lcg_state) {
+               /* Record up to max_hits intersections. */
+               for(int i = min(max_hits, local_isect->num_hits) - 1; i >= 0; --i) {
+                       if(local_isect->hits[i].t == t) {
+                               return;
+                       }
                }
-       }
 
-       local_isect->num_hits++;
-       int hit;
+               local_isect->num_hits++;
+
+               if(local_isect->num_hits <= max_hits) {
+                       hit = local_isect->num_hits - 1;
+               }
+               else {
+                       /* reservoir sampling: if we are at the maximum number of
+                        * hits, randomly replace element or skip it */
+                       hit = lcg_step_uint(lcg_state) % local_isect->num_hits;
 
-       if(local_isect->num_hits <= max_hits) {
-               hit = local_isect->num_hits - 1;
+                       if(hit >= max_hits)
+                               return;
+               }
        }
        else {
-               /* reservoir sampling: if we are at the maximum number of
-                * hits, randomly replace element or skip it */
-               hit = lcg_step_uint(lcg_state) % local_isect->num_hits;
-
-               if(hit >= max_hits)
+               /* Record closest intersection only. */
+               if(local_isect->num_hits && t > local_isect->hits[0].t) {
                        return;
+               }
+
+               hit = 0;
+               local_isect->num_hits = 1;
        }
 
-       /* record intersection */
+       /* Record intersection. */
        Intersection *isect = &local_isect->hits[hit];
        isect->prim = prim_addr;
        isect->object = object;
index afca4575331c93c2cd7f67d9db5c77c05c3cc2fa..dbbb80ca37ff7192452b86bd401176532dc8adc3 100644 (file)
 #include "kernel/kernel_light.h"
 #include "kernel/kernel_passes.h"
 
-#ifdef __SUBSURFACE__
-#  include "kernel/kernel_subsurface.h"
+#if defined(__VOLUME__) || defined(__SUBSURFACE__)
+#  include "kernel/kernel_volume.h"
 #endif
 
-#ifdef __VOLUME__
-#  include "kernel/kernel_volume.h"
+#ifdef __SUBSURFACE__
+#  include "kernel/kernel_subsurface.h"
 #endif
 
 #include "kernel/kernel_path_state.h"
index 5f917d509ec90805f46672c7c58529406c224e89..6fb55bda08dcb37c24b1ff1dbf51da93569f2bcf 100644 (file)
@@ -350,6 +350,7 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg,
                        int num_hits = subsurface_scatter_multi_intersect(kg,
                                                                          &ss_isect,
                                                                          sd,
+                                                                         &hit_state,
                                                                          sc,
                                                                          &lcg_state,
                                                                          bssrdf_u, bssrdf_v,
index a48bde6443e7796377aa4550c7342018f94adf3a..71aea9e3b272e44194546c62123252bfaeda432b 100644 (file)
@@ -51,6 +51,7 @@ bool kernel_path_subsurface_scatter(
                int num_hits = subsurface_scatter_multi_intersect(kg,
                                                                  &ss_isect,
                                                                  sd,
+                                                                 state,
                                                                  sc,
                                                                  &lcg_state,
                                                                  bssrdf_u, bssrdf_v,
index f4759b26191e25b3c5b396439be9fded9528d769..a0dba7e13868574fc51da373bcd0cec059ff3690 100644 (file)
@@ -20,7 +20,6 @@ CCL_NAMESPACE_BEGIN
  *
  * BSSRDF Importance Sampling, SIGGRAPH 2013
  * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf
- *
  */
 
 ccl_device_inline float3 subsurface_scatter_eval(ShaderData *sd,
@@ -41,7 +40,7 @@ ccl_device_inline float3 subsurface_scatter_eval(ShaderData *sd,
                for(int i = 0; i < sd->num_closure; i++) {
                        sc = &sd->closure[i];
 
-                       if(CLOSURE_IS_BSSRDF(sc->type)) {
+                       if(CLOSURE_IS_DISK_BSSRDF(sc->type)) {
                                sample_weight_sum += sc->sample_weight;
                        }
                }
@@ -52,7 +51,7 @@ ccl_device_inline float3 subsurface_scatter_eval(ShaderData *sd,
        for(int i = 0; i < sd->num_closure; i++) {
                sc = &sd->closure[i];
                
-               if(CLOSURE_IS_BSSRDF(sc->type)) {
+               if(CLOSURE_IS_DISK_BSSRDF(sc->type)) {
                        /* in case of branched path integrate we sample all bssrdf's once,
                         * for path trace we pick one, so adjust pdf for that */
                        float sample_weight = (all)? 1.0f: sc->sample_weight * sample_weight_inv;
@@ -166,7 +165,7 @@ ccl_device void subsurface_color_bump_blur(KernelGlobals *kg,
 /* Subsurface scattering step, from a point on the surface to other
  * nearby points on the same object.
  */
-ccl_device_inline int subsurface_scatter_multi_intersect(
+ccl_device_inline int subsurface_scatter_disk(
         KernelGlobals *kg,
         LocalIntersection *ss_isect,
         ShaderData *sd,
@@ -433,5 +432,202 @@ ccl_device void subsurface_scatter_step(KernelGlobals *kg, ShaderData *sd, ccl_a
        subsurface_scatter_setup_diffuse_bsdf(kg, sd, sc, eval, (ss_isect.num_hits > 0), N);
 }
 
+/* Random walk subsurface scattering.
+ *
+ * "Practical and Controllable Subsurface Scattering for Production Path
+ *  Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */
+
+ccl_device void subsurface_random_walk_remap(
+        const float A,
+        const float d,
+        float *sigma_t,
+        float *sigma_s)
+{
+       /* Compute attenuation and scattering coefficients from albedo. */
+       const float a = 1.0f - expf(A * (-5.09406f + A * (2.61188f - A * 4.31805f)));
+       const float s = 1.9f - A + 3.5f * sqr(A - 0.8f);
+
+       *sigma_t = 1.0f / fmaxf(d * s, 1e-16f);
+       *sigma_s = *sigma_t * a;
+}
+
+ccl_device void subsurface_random_walk_coefficients(
+        const ShaderClosure *sc,
+        float3 *sigma_t,
+        float3 *sigma_s,
+        float3 *weight)
+{
+       const Bssrdf *bssrdf = (const Bssrdf*)sc;
+       const float3 A = bssrdf->albedo;
+       const float3 d = bssrdf->radius;
+       float sigma_t_x, sigma_t_y, sigma_t_z;
+       float sigma_s_x, sigma_s_y, sigma_s_z;
+
+       subsurface_random_walk_remap(A.x, d.x, &sigma_t_x, &sigma_s_x);
+       subsurface_random_walk_remap(A.y, d.y, &sigma_t_y, &sigma_s_y);
+       subsurface_random_walk_remap(A.z, d.z, &sigma_t_z, &sigma_s_z);
+
+       *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z);
+       *sigma_s = make_float3(sigma_s_x, sigma_s_y, sigma_s_z);
+
+       /* Closure mixing and Fresnel weights separate from albedo. */
+       *weight = safe_divide_color(bssrdf->weight, A);
+}
+
+ccl_device_noinline bool subsurface_random_walk(
+        KernelGlobals *kg,
+        LocalIntersection *ss_isect,
+        ShaderData *sd,
+        ccl_addr_space PathState *state,
+        const ShaderClosure *sc,
+        const float bssrdf_u,
+        const float bssrdf_v)
+{
+       /* Sample diffuse surface scatter into the object. */
+       float3 D;
+       float pdf;
+       sample_cos_hemisphere(-sd->N, bssrdf_u, bssrdf_v, &D, &pdf);
+       if(dot(-sd->Ng, D) <= 0.0f) {
+               return 0;
+       }
+
+       /* Convert subsurface to volume coefficients. */
+       float3 sigma_t, sigma_s;
+       float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
+       subsurface_random_walk_coefficients(sc, &sigma_t, &sigma_s, &throughput);
+
+       /* Setup ray. */
+#ifdef __SPLIT_KERNEL__
+       Ray ray_object = ss_isect->ray;
+       Ray *ray = &ray_object;
+#else
+       Ray *ray = &ss_isect->ray;
+#endif
+       ray->P = ray_offset(sd->P, -sd->Ng);
+       ray->D = D;
+       ray->t = FLT_MAX;
+       ray->time = sd->time;
+
+       /* Modify state for RNGs, decorrelated from other paths. */
+       uint prev_rng_offset = state->rng_offset;
+       uint prev_rng_hash = state->rng_hash;
+       state->rng_hash = cmj_hash(state->rng_hash + state->rng_offset, 0xdeadbeef);
+
+       /* Random walk until we hit the surface again. */
+       bool hit = false;
+
+       for(int bounce = 0; bounce < BSSRDF_MAX_BOUNCES; bounce++) {
+               /* Advance random number offset. */
+               state->rng_offset += PRNG_BOUNCE_NUM;
+
+               if(bounce > 0) {
+                       /* Sample scattering direction. */
+                       const float anisotropy = 0.0f;
+                       float scatter_u, scatter_v;
+                       path_state_rng_2D(kg, state, PRNG_BSDF_U, &scatter_u, &scatter_v);
+                       ray->D = henyey_greenstrein_sample(ray->D, anisotropy, scatter_u, scatter_v, NULL);
+               }
+
+               /* Sample color channel, use MIS with balance heuristic. */
+               float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL);
+               float3 albedo = safe_divide_color(sigma_s, sigma_t);
+               float3 channel_pdf;
+               int channel = kernel_volume_sample_channel(albedo, throughput, rphase, &channel_pdf);
+
+               /* Distance sampling. */
+               float rdist = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE);
+               float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel);
+               float t = -logf(1.0f - rdist)/sample_sigma_t;
+
+               ray->t = t;
+               scene_intersect_local(kg, *ray, ss_isect, sd->object, NULL, 1);
+               hit = (ss_isect->num_hits > 0);
+
+               if(hit) {
+                       /* Compute world space distance to surface hit. */
+                       float3 D = ray->D;
+                       object_inverse_dir_transform(kg, sd, &D);
+                       D = normalize(D) * ss_isect->hits[0].t;
+                       object_dir_transform(kg, sd, &D);
+                       t = len(D);
+               }
+
+               /* Advance to new scatter location. */
+               ray->P += t * ray->D;
+
+               /* Update throughput. */
+               float3 transmittance = volume_color_transmittance(sigma_t, t);
+               float pdf = dot(channel_pdf, (hit)? transmittance: sigma_t * transmittance);
+               throughput *= ((hit)? transmittance: sigma_s * transmittance) / pdf;
+
+               if(hit) {
+                       /* If we hit the surface, we are done. */
+                       break;
+               }
+
+               /* Russian roulette. */
+               float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE);
+               float probability = min(max3(fabs(throughput)), 1.0f);
+               if(terminate >= probability) {
+                       break;
+               }
+               throughput /= probability;
+       }
+
+       kernel_assert(isfinite_safe(throughput.x) &&
+                     isfinite_safe(throughput.y) &&
+                     isfinite_safe(throughput.z));
+
+       state->rng_offset = prev_rng_offset;
+       state->rng_hash = prev_rng_hash;
+
+       /* Return number of hits in ss_isect. */
+       if(!hit) {
+               return 0;
+       }
+
+       /* TODO: gain back performance lost from merging with disk BSSRDF. We
+        * only need to return on hit so this indirect ray push/pop overhead
+        * is not actually needed, but it does keep the code simpler. */
+       ss_isect->weight[0] = throughput;
+#ifdef __SPLIT_KERNEL__
+       ss_isect->ray = *ray;
+#endif
+
+       return 1;
+}
+
+ccl_device_inline int subsurface_scatter_multi_intersect(
+        KernelGlobals *kg,
+        LocalIntersection *ss_isect,
+        ShaderData *sd,
+        ccl_addr_space PathState *state,
+        const ShaderClosure *sc,
+        uint *lcg_state,
+        float bssrdf_u,
+        float bssrdf_v,
+        bool all)
+{
+       if(CLOSURE_IS_DISK_BSSRDF(sc->type)) {
+               return subsurface_scatter_disk(kg,
+                                              ss_isect,
+                                              sd,
+                                              sc,
+                                              lcg_state,
+                                              bssrdf_u,
+                                              bssrdf_v,
+                                              all);
+       }
+       else {
+               return subsurface_random_walk(kg,
+                                             ss_isect,
+                                             sd,
+                                             state,
+                                             sc,
+                                             bssrdf_u,
+                                             bssrdf_v);
+       }
+}
+
 CCL_NAMESPACE_END
 
index 1e1a4f34650c046202d3401b4705232505654ec1..cd3b450932f119df0c3923d38dec56391eb1ccb0 100644 (file)
@@ -46,6 +46,7 @@ CCL_NAMESPACE_BEGIN
 
 #define BSSRDF_MIN_RADIUS                      1e-8f
 #define BSSRDF_MAX_HITS                                4
+#define BSSRDF_MAX_BOUNCES                     256
 #define LOCAL_MAX_HITS                         4
 
 #define BECKMANN_TABLE_SIZE            256
index 2af4c9a5e7a6b3a9920993fdfe947a91fa4120c0..7b67a37adc5b7c2b080d33ab82d662e51ca15171 100644 (file)
@@ -35,6 +35,8 @@ typedef struct VolumeShaderCoefficients {
        float3 emission;
 } VolumeShaderCoefficients;
 
+#ifdef __VOLUME__
+
 /* evaluate shader to get extinction coefficient at P */
 ccl_device_inline bool volume_shader_extinction_sample(KernelGlobals *kg,
                                                        ShaderData *sd,
@@ -92,6 +94,8 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals *kg,
        return true;
 }
 
+#endif /* __VOLUME__ */
+
 ccl_device float3 volume_color_transmittance(float3 sigma, float t)
 {
        return make_float3(expf(-sigma.x * t), expf(-sigma.y * t), expf(-sigma.z * t));
@@ -102,6 +106,8 @@ ccl_device float kernel_volume_channel_get(float3 value, int channel)
        return (channel == 0)? value.x: ((channel == 1)? value.y: value.z);
 }
 
+#ifdef __VOLUME__
+
 ccl_device bool volume_stack_is_heterogeneous(KernelGlobals *kg, ccl_addr_space VolumeStack *stack)
 {
        for(int i = 0; stack[i].shader != SHADER_NONE; i++) {
@@ -239,6 +245,8 @@ ccl_device_noinline void kernel_volume_shadow(KernelGlobals *kg,
                kernel_volume_shadow_homogeneous(kg, state, ray, shadow_sd, throughput);
 }
 
+#endif /* __VOLUME__ */
+
 /* Equi-angular sampling as in:
  * "Importance Sampling Techniques for Path Tracing in Participating Media" */
 
@@ -369,6 +377,8 @@ ccl_device int kernel_volume_sample_channel(float3 albedo, float3 throughput, fl
        }
 }
 
+#ifdef __VOLUME__
+
 /* homogeneous volume: assume shader evaluation at the start gives
  * the volume shading coefficient for the entire line segment */
 ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(
@@ -1346,4 +1356,6 @@ ccl_device_inline void kernel_volume_clean_stack(KernelGlobals *kg,
        }
 }
 
+#endif /* __VOLUME__ */
+
 CCL_NAMESPACE_END
index db6426f60e5d114e2a3deb9857c1864c2c4c6f5f..907afe7d17a5e535fbcc4c23ab177049aee11af1 100644 (file)
@@ -52,6 +52,7 @@ static ustring u_cubic("cubic");
 static ustring u_gaussian("gaussian");
 static ustring u_burley("burley");
 static ustring u_principled("principled");
+static ustring u_random_walk("random_walk");
 
 class CBSSRDFClosure : public CClosurePrimitive {
 public:
@@ -79,6 +80,9 @@ public:
                else if (method == u_principled) {
                        alloc(sd, path_flag, weight, CLOSURE_BSSRDF_PRINCIPLED_ID);
                }
+               else if (method == u_random_walk) {
+                       alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_ID);
+               }
        }
 
        void alloc(ShaderData *sd, int path_flag, float3 weight, ClosureType type)
index c9983fcd5ddfbe3b70133c4c2a5047008db24928..0df3256e1fdedc1aae7cc2ba6839ac1ff8a70781 100644 (file)
@@ -30,7 +30,9 @@ shader node_subsurface_scattering(
                BSSRDF = Color * bssrdf("gaussian", Normal, Scale * Radius, Color, "texture_blur", TextureBlur);
        else if (falloff == "cubic")
                BSSRDF = Color * bssrdf("cubic", Normal, Scale * Radius, Color, "texture_blur", TextureBlur, "sharpness", Sharpness);
-       else
+       else if (falloff == "burley")
                BSSRDF = Color * bssrdf("burley", Normal, Scale * Radius, Color, "texture_blur", TextureBlur);
+       else
+               BSSRDF = Color * bssrdf("random_walk", Normal, Scale * Radius, Color, "texture_blur", TextureBlur);
 }
 
index f902d0009187aed4c9345acb25eaaebc53be876d..e50d63ea3bc6d5900463ebc8cd502b5f3e94d804 100644 (file)
@@ -85,6 +85,7 @@ ccl_device_noinline bool kernel_split_branched_path_subsurface_indirect_light_it
                                branched_state->num_hits = subsurface_scatter_multi_intersect(kg,
                                                                                              &ss_isect_private,
                                                                                              sd,
+                                                                                             hit_state,
                                                                                              sc,
                                                                                              &lcg_state,
                                                                                              bssrdf_u, bssrdf_v,
index 578434792e4af77b06efe0c609f1b29aac24d9d8..f013dc396d08a29b7ac0eb40ab841a3bb9d55659 100644 (file)
@@ -764,7 +764,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *
 #ifdef __SUBSURFACE__
                case CLOSURE_BSSRDF_CUBIC_ID:
                case CLOSURE_BSSRDF_GAUSSIAN_ID:
-               case CLOSURE_BSSRDF_BURLEY_ID: {
+               case CLOSURE_BSSRDF_BURLEY_ID:
+               case CLOSURE_BSSRDF_RANDOM_WALK_ID: {
                        float3 weight = sd->svm_closure_weight * mix_weight;
                        Bssrdf *bssrdf = bssrdf_alloc(sd, weight);
 
index 6ff04a65462dba448a5aeae5d3af95d8bccbeebc..9a87b4ee3586c6b7f772f35f5f9ae0776b0f0a7d 100644 (file)
@@ -446,6 +446,7 @@ typedef enum ClosureType {
        CLOSURE_BSSRDF_GAUSSIAN_ID,
        CLOSURE_BSSRDF_PRINCIPLED_ID,
        CLOSURE_BSSRDF_BURLEY_ID,
+       CLOSURE_BSSRDF_RANDOM_WALK_ID,
 
        /* Other */
        CLOSURE_HOLDOUT_ID,
@@ -477,8 +478,9 @@ typedef enum ClosureType {
 #define CLOSURE_IS_BSDF_MICROFACET(type) ((type >= CLOSURE_BSDF_MICROFACET_GGX_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID) ||\
                                           (type >= CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID && type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID) ||\
                                           (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID))
-#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_BURLEY_ID)
-#define CLOSURE_IS_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_BURLEY_ID)
+#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_RANDOM_WALK_ID)
+#define CLOSURE_IS_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_ID)
+#define CLOSURE_IS_DISK_BSSRDF(type) (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_BURLEY_ID)
 #define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID)
 #define CLOSURE_IS_VOLUME_SCATTER(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID)
 #define CLOSURE_IS_VOLUME_ABSORPTION(type) (type == CLOSURE_VOLUME_ABSORPTION_ID)
index 0dc1a9aa0533f45d1691d6b4b1b217dda8fc29f6..c337c19ced18473cae122351a532af3c10d2e64f 100644 (file)
@@ -187,7 +187,10 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
                max_samples = max(max_samples, volume_samples);
        }
 
-       max_samples *= (max_bounce + transparent_max_bounce + 3 + BSSRDF_MAX_HITS);
+       uint total_bounces = max_bounce + transparent_max_bounce + 3 +
+                            max(BSSRDF_MAX_HITS, BSSRDF_MAX_BOUNCES);
+
+       max_samples *= total_bounces;
 
        int dimensions = PRNG_BASE_NUM + max_samples*PRNG_BOUNCE_NUM;
        dimensions = min(dimensions, SOBOL_MAX_DIMENSIONS);
index acfe07bf112eb7799f8240065dae55599abc955e..7e8298e09c1939fa0cc65f96978b89d1f3529363 100644 (file)
@@ -2525,6 +2525,7 @@ NODE_DEFINE(SubsurfaceScatteringNode)
        falloff_enum.insert("cubic", CLOSURE_BSSRDF_CUBIC_ID);
        falloff_enum.insert("gaussian", CLOSURE_BSSRDF_GAUSSIAN_ID);
        falloff_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID);
+       falloff_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID);
        SOCKET_ENUM(falloff, "Falloff", falloff_enum, CLOSURE_BSSRDF_BURLEY_ID);
        SOCKET_IN_FLOAT(scale, "Scale", 0.01f);
        SOCKET_IN_VECTOR(radius, "Radius", make_float3(0.1f, 0.1f, 0.1f));
index e73e5bc17a225cd9b38f7e288dfb7fd82277f509..e0a89b539b0012807dcd0c9cafb2aa2e88fdf9b1 100644 (file)
@@ -59,6 +59,7 @@ ccl_device_inline float3 mix(const float3& a, const float3& b, float t);
 ccl_device_inline float3 rcp(const float3& a);
 #endif  /* !__KERNEL_OPENCL__ */
 
+ccl_device_inline float min3(float3 a);
 ccl_device_inline float max3(float3 a);
 ccl_device_inline float len(const float3 a);
 ccl_device_inline float len_squared(const float3 a);
@@ -285,6 +286,11 @@ ccl_device_inline float3 rcp(const float3& a)
 }
 #endif  /* !__KERNEL_OPENCL__ */
 
+ccl_device_inline float min3(float3 a)
+{
+       return min(min(a.x, a.y), a.z);
+}
+
 ccl_device_inline float max3(float3 a)
 {
        return max(max(a.x, a.y), a.z);
index 4dbf3a354ce715682a82d2ca694d953bbcfc24e0..8ae1a79f8f7097e3bf5ccf316b274a850a69e6c8 100644 (file)
@@ -1074,6 +1074,7 @@ enum {
        SHD_SUBSURFACE_CUBIC                    = 1,
        SHD_SUBSURFACE_GAUSSIAN                 = 2,
        SHD_SUBSURFACE_BURLEY                   = 3,
+       SHD_SUBSURFACE_RANDOM_WALK              = 4,
 };
 
 /* blur node */
index 4225aa38d572ecef78bed22d7bbfca1ebd632c83..bbe78d43f505e22b2f0289ca7f12d6a4dc6d37ea 100644 (file)
@@ -4444,6 +4444,7 @@ static void def_sh_subsurface(StructRNA *srna)
                {SHD_SUBSURFACE_CUBIC, "CUBIC", 0, "Cubic", "Simple cubic falloff function"},
                {SHD_SUBSURFACE_GAUSSIAN, "GAUSSIAN", 0, "Gaussian", "Normal distribution, multiple can be combined to fit more complex profiles"},
                {SHD_SUBSURFACE_BURLEY, "BURLEY", 0, "Christensen-Burley", "Approximation to physically based volume scattering"},
+               {SHD_SUBSURFACE_RANDOM_WALK, "RANDOM_WALK", 0, "Random Walk", "Volumetric approximation to physically based volume scattering"},
                {0, NULL, 0, NULL, NULL}
        };