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 /intern/cycles/kernel/kernel_subsurface.h
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
Diffstat (limited to 'intern/cycles/kernel/kernel_subsurface.h')
-rw-r--r--intern/cycles/kernel/kernel_subsurface.h204
1 files changed, 200 insertions, 4 deletions
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