Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2018-01-21 16:04:22 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2018-02-09 21:58:33 +0300
commit0df9b2c71517a98760a5e577f434d9d86e4e1910 (patch)
treeeee5839a1e6408af8c0e1766f37e68a8aadfbfb2
parent3ab5ef7b4f34f110e4861096428b83b4f9b5efe9 (diff)
Cycles: random walk subsurface scattering.
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
-rw-r--r--intern/cycles/blender/blender_shader.cpp3
-rw-r--r--intern/cycles/kernel/closure/bsdf.h4
-rw-r--r--intern/cycles/kernel/closure/bssrdf.h3
-rw-r--r--intern/cycles/kernel/closure/volume.h40
-rw-r--r--intern/cycles/kernel/geom/geom_motion_triangle_intersect.h54
-rw-r--r--intern/cycles/kernel/geom/geom_triangle_intersect.h40
-rw-r--r--intern/cycles/kernel/kernel_path.h8
-rw-r--r--intern/cycles/kernel/kernel_path_branched.h1
-rw-r--r--intern/cycles/kernel/kernel_path_subsurface.h1
-rw-r--r--intern/cycles/kernel/kernel_subsurface.h204
-rw-r--r--intern/cycles/kernel/kernel_types.h1
-rw-r--r--intern/cycles/kernel/kernel_volume.h12
-rw-r--r--intern/cycles/kernel/osl/osl_bssrdf.cpp4
-rw-r--r--intern/cycles/kernel/shaders/node_subsurface_scattering.osl4
-rw-r--r--intern/cycles/kernel/split/kernel_subsurface_scatter.h1
-rw-r--r--intern/cycles/kernel/svm/svm_closure.h3
-rw-r--r--intern/cycles/kernel/svm/svm_types.h6
-rw-r--r--intern/cycles/render/integrator.cpp5
-rw-r--r--intern/cycles/render/nodes.cpp1
-rw-r--r--intern/cycles/util/util_math_float3.h6
-rw-r--r--source/blender/makesdna/DNA_node_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c1
22 files changed, 339 insertions, 64 deletions
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index ac1eba85dbb..d6f7a08431d 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -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;
diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h
index 6f0bdb3fa38..e3beff6675a 100644
--- a/intern/cycles/kernel/closure/bsdf.h
+++ b/intern/cycles/kernel/closure/bsdf.h
@@ -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
diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h
index c8f505e8418..790368ee888 100644
--- a/intern/cycles/kernel/closure/bssrdf.h
+++ b/intern/cycles/kernel/closure/bssrdf.h
@@ -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);
}
diff --git a/intern/cycles/kernel/closure/volume.h b/intern/cycles/kernel/closure/volume.h
index 4bb5e680723..da791e9aa73 100644
--- a/intern/cycles/kernel/closure/volume.h
+++ b/intern/cycles/kernel/closure/volume.h
@@ -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__
diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h
index 4a19f2ba031..542843edc84 100644
--- a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h
+++ b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h
@@ -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__ */
diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h
index 7daa378f81e..a3b23115ae4 100644
--- a/intern/cycles/kernel/geom/geom_triangle_intersect.h
+++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h
@@ -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;
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index afca4575331..dbbb80ca37f 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -32,12 +32,12 @@
#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"
diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h
index 5f917d509ec..6fb55bda08d 100644
--- a/intern/cycles/kernel/kernel_path_branched.h
+++ b/intern/cycles/kernel/kernel_path_branched.h
@@ -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,
diff --git a/intern/cycles/kernel/kernel_path_subsurface.h b/intern/cycles/kernel/kernel_path_subsurface.h
index a48bde6443e..71aea9e3b27 100644
--- a/intern/cycles/kernel/kernel_path_subsurface.h
+++ b/intern/cycles/kernel/kernel_path_subsurface.h
@@ -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,
diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h
index f4759b26191..a0dba7e1386 100644
--- a/intern/cycles/kernel/kernel_subsurface.h
+++ b/intern/cycles/kernel/kernel_subsurface.h
@@ -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
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 1e1a4f34650..cd3b450932f 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -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
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index 2af4c9a5e7a..7b67a37adc5 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -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
diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp
index db6426f60e5..907afe7d17a 100644
--- a/intern/cycles/kernel/osl/osl_bssrdf.cpp
+++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp
@@ -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)
diff --git a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl
index c9983fcd5dd..0df3256e1fd 100644
--- a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl
+++ b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl
@@ -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);
}
diff --git a/intern/cycles/kernel/split/kernel_subsurface_scatter.h b/intern/cycles/kernel/split/kernel_subsurface_scatter.h
index f902d000918..e50d63ea3bc 100644
--- a/intern/cycles/kernel/split/kernel_subsurface_scatter.h
+++ b/intern/cycles/kernel/split/kernel_subsurface_scatter.h
@@ -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,
diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h
index 578434792e4..f013dc396d0 100644
--- a/intern/cycles/kernel/svm/svm_closure.h
+++ b/intern/cycles/kernel/svm/svm_closure.h
@@ -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);
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index 6ff04a65462..9a87b4ee358 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -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)
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index 0dc1a9aa053..c337c19ced1 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -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);
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index acfe07bf112..7e8298e09c1 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -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));
diff --git a/intern/cycles/util/util_math_float3.h b/intern/cycles/util/util_math_float3.h
index e73e5bc17a2..e0a89b539b0 100644
--- a/intern/cycles/util/util_math_float3.h
+++ b/intern/cycles/util/util_math_float3.h
@@ -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);
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 4dbf3a354ce..8ae1a79f8f7 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1074,6 +1074,7 @@ enum {
SHD_SUBSURFACE_CUBIC = 1,
SHD_SUBSURFACE_GAUSSIAN = 2,
SHD_SUBSURFACE_BURLEY = 3,
+ SHD_SUBSURFACE_RANDOM_WALK = 4,
};
/* blur node */
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 4225aa38d57..bbe78d43f50 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -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}
};