diff options
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} }; |