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:
Diffstat (limited to 'intern/cycles/kernel/kernel_subsurface.h')
-rw-r--r--intern/cycles/kernel/kernel_subsurface.h822
1 files changed, 391 insertions, 431 deletions
diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h
index 96b717530ce..7510e50a962 100644
--- a/intern/cycles/kernel/kernel_subsurface.h
+++ b/intern/cycles/kernel/kernel_subsurface.h
@@ -22,317 +22,295 @@ CCL_NAMESPACE_BEGIN
* http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf
*/
-ccl_device_inline float3 subsurface_scatter_eval(ShaderData *sd,
- const ShaderClosure *sc,
- float disk_r,
- float r,
- bool all)
+ccl_device_inline float3
+subsurface_scatter_eval(ShaderData *sd, const ShaderClosure *sc, float disk_r, float r, bool all)
{
- /* this is the veach one-sample model with balance heuristic, some pdf
- * factors drop out when using balance heuristic weighting */
- float3 eval_sum = make_float3(0.0f, 0.0f, 0.0f);
- float pdf_sum = 0.0f;
- float sample_weight_inv = 0.0f;
+ /* this is the veach one-sample model with balance heuristic, some pdf
+ * factors drop out when using balance heuristic weighting */
+ float3 eval_sum = make_float3(0.0f, 0.0f, 0.0f);
+ float pdf_sum = 0.0f;
+ float sample_weight_inv = 0.0f;
- if(!all) {
- float sample_weight_sum = 0.0f;
+ if (!all) {
+ float sample_weight_sum = 0.0f;
- for(int i = 0; i < sd->num_closure; i++) {
- sc = &sd->closure[i];
+ for (int i = 0; i < sd->num_closure; i++) {
+ sc = &sd->closure[i];
- if(CLOSURE_IS_DISK_BSSRDF(sc->type)) {
- sample_weight_sum += sc->sample_weight;
- }
- }
+ if (CLOSURE_IS_DISK_BSSRDF(sc->type)) {
+ sample_weight_sum += sc->sample_weight;
+ }
+ }
- sample_weight_inv = 1.0f/sample_weight_sum;
- }
+ sample_weight_inv = 1.0f / sample_weight_sum;
+ }
- for(int i = 0; i < sd->num_closure; i++) {
- sc = &sd->closure[i];
+ for (int i = 0; i < sd->num_closure; i++) {
+ sc = &sd->closure[i];
- 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;
+ 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;
- /* compute pdf */
- float3 eval = bssrdf_eval(sc, r);
- float pdf = bssrdf_pdf(sc, disk_r);
+ /* compute pdf */
+ float3 eval = bssrdf_eval(sc, r);
+ float pdf = bssrdf_pdf(sc, disk_r);
- eval_sum += sc->weight * eval;
- pdf_sum += sample_weight * pdf;
- }
- }
+ eval_sum += sc->weight * eval;
+ pdf_sum += sample_weight * pdf;
+ }
+ }
- return (pdf_sum > 0.0f)? eval_sum / pdf_sum : make_float3(0.0f, 0.0f, 0.0f);
+ return (pdf_sum > 0.0f) ? eval_sum / pdf_sum : make_float3(0.0f, 0.0f, 0.0f);
}
/* replace closures with a single diffuse bsdf closure after scatter step */
-ccl_device void subsurface_scatter_setup_diffuse_bsdf(KernelGlobals *kg, ShaderData *sd, ClosureType type, float roughness, float3 weight, float3 N)
+ccl_device void subsurface_scatter_setup_diffuse_bsdf(
+ KernelGlobals *kg, ShaderData *sd, ClosureType type, float roughness, float3 weight, float3 N)
{
- sd->flag &= ~SD_CLOSURE_FLAGS;
- sd->num_closure = 0;
- sd->num_closure_left = kernel_data.integrator.max_closures;
+ sd->flag &= ~SD_CLOSURE_FLAGS;
+ sd->num_closure = 0;
+ sd->num_closure_left = kernel_data.integrator.max_closures;
#ifdef __PRINCIPLED__
- if(type == CLOSURE_BSSRDF_PRINCIPLED_ID ||
- type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID)
- {
- PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf*)bsdf_alloc(sd, sizeof(PrincipledDiffuseBsdf), weight);
-
- if(bsdf) {
- bsdf->N = N;
- bsdf->roughness = roughness;
- sd->flag |= bsdf_principled_diffuse_setup(bsdf);
-
- /* replace CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID with this special ID so render passes
- * can recognize it as not being a regular Disney principled diffuse closure */
- bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID;
- }
- }
- else if(CLOSURE_IS_BSDF_BSSRDF(type) ||
- CLOSURE_IS_BSSRDF(type))
-#endif /* __PRINCIPLED__ */
- {
- DiffuseBsdf *bsdf = (DiffuseBsdf*)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight);
-
- if(bsdf) {
- bsdf->N = N;
- sd->flag |= bsdf_diffuse_setup(bsdf);
-
- /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes
- * can recognize it as not being a regular diffuse closure */
- bsdf->type = CLOSURE_BSDF_BSSRDF_ID;
- }
- }
+ if (type == CLOSURE_BSSRDF_PRINCIPLED_ID || type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) {
+ PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc(
+ sd, sizeof(PrincipledDiffuseBsdf), weight);
+
+ if (bsdf) {
+ bsdf->N = N;
+ bsdf->roughness = roughness;
+ sd->flag |= bsdf_principled_diffuse_setup(bsdf);
+
+ /* replace CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID with this special ID so render passes
+ * can recognize it as not being a regular Disney principled diffuse closure */
+ bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID;
+ }
+ }
+ else if (CLOSURE_IS_BSDF_BSSRDF(type) || CLOSURE_IS_BSSRDF(type))
+#endif /* __PRINCIPLED__ */
+ {
+ DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight);
+
+ if (bsdf) {
+ bsdf->N = N;
+ sd->flag |= bsdf_diffuse_setup(bsdf);
+
+ /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes
+ * can recognize it as not being a regular diffuse closure */
+ bsdf->type = CLOSURE_BSDF_BSSRDF_ID;
+ }
+ }
}
/* optionally do blurring of color and/or bump mapping, at the cost of a shader evaluation */
ccl_device float3 subsurface_color_pow(float3 color, float exponent)
{
- color = max(color, make_float3(0.0f, 0.0f, 0.0f));
-
- if(exponent == 1.0f) {
- /* nothing to do */
- }
- else if(exponent == 0.5f) {
- color.x = sqrtf(color.x);
- color.y = sqrtf(color.y);
- color.z = sqrtf(color.z);
- }
- else {
- color.x = powf(color.x, exponent);
- color.y = powf(color.y, exponent);
- color.z = powf(color.z, exponent);
- }
-
- return color;
+ color = max(color, make_float3(0.0f, 0.0f, 0.0f));
+
+ if (exponent == 1.0f) {
+ /* nothing to do */
+ }
+ else if (exponent == 0.5f) {
+ color.x = sqrtf(color.x);
+ color.y = sqrtf(color.y);
+ color.z = sqrtf(color.z);
+ }
+ else {
+ color.x = powf(color.x, exponent);
+ color.y = powf(color.y, exponent);
+ color.z = powf(color.z, exponent);
+ }
+
+ return color;
}
-ccl_device void subsurface_color_bump_blur(KernelGlobals *kg,
- ShaderData *sd,
- ccl_addr_space PathState *state,
- float3 *eval,
- float3 *N)
+ccl_device void subsurface_color_bump_blur(
+ KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, float3 *eval, float3 *N)
{
- /* average color and texture blur at outgoing point */
- float texture_blur;
- float3 out_color = shader_bssrdf_sum(sd, NULL, &texture_blur);
-
- /* do we have bump mapping? */
- bool bump = (sd->flag & SD_HAS_BSSRDF_BUMP) != 0;
-
- if(bump || texture_blur > 0.0f) {
- /* average color and normal at incoming point */
- shader_eval_surface(kg, sd, state, state->flag);
- float3 in_color = shader_bssrdf_sum(sd, (bump)? N: NULL, NULL);
-
- /* we simply divide out the average color and multiply with the average
- * of the other one. we could try to do this per closure but it's quite
- * tricky to match closures between shader evaluations, their number and
- * order may change, this is simpler */
- if(texture_blur > 0.0f) {
- out_color = subsurface_color_pow(out_color, texture_blur);
- in_color = subsurface_color_pow(in_color, texture_blur);
-
- *eval *= safe_divide_color(in_color, out_color);
- }
- }
+ /* average color and texture blur at outgoing point */
+ float texture_blur;
+ float3 out_color = shader_bssrdf_sum(sd, NULL, &texture_blur);
+
+ /* do we have bump mapping? */
+ bool bump = (sd->flag & SD_HAS_BSSRDF_BUMP) != 0;
+
+ if (bump || texture_blur > 0.0f) {
+ /* average color and normal at incoming point */
+ shader_eval_surface(kg, sd, state, state->flag);
+ float3 in_color = shader_bssrdf_sum(sd, (bump) ? N : NULL, NULL);
+
+ /* we simply divide out the average color and multiply with the average
+ * of the other one. we could try to do this per closure but it's quite
+ * tricky to match closures between shader evaluations, their number and
+ * order may change, this is simpler */
+ if (texture_blur > 0.0f) {
+ out_color = subsurface_color_pow(out_color, texture_blur);
+ in_color = subsurface_color_pow(in_color, texture_blur);
+
+ *eval *= safe_divide_color(in_color, out_color);
+ }
+ }
}
/* Subsurface scattering step, from a point on the surface to other
* nearby points on the same object.
*/
-ccl_device_inline int subsurface_scatter_disk(
- KernelGlobals *kg,
- LocalIntersection *ss_isect,
- ShaderData *sd,
- const ShaderClosure *sc,
- uint *lcg_state,
- float disk_u,
- float disk_v,
- bool all)
+ccl_device_inline int subsurface_scatter_disk(KernelGlobals *kg,
+ LocalIntersection *ss_isect,
+ ShaderData *sd,
+ const ShaderClosure *sc,
+ uint *lcg_state,
+ float disk_u,
+ float disk_v,
+ bool all)
{
- /* pick random axis in local frame and point on disk */
- float3 disk_N, disk_T, disk_B;
- float pick_pdf_N, pick_pdf_T, pick_pdf_B;
-
- disk_N = sd->Ng;
- make_orthonormals(disk_N, &disk_T, &disk_B);
-
- if(disk_v < 0.5f) {
- pick_pdf_N = 0.5f;
- pick_pdf_T = 0.25f;
- pick_pdf_B = 0.25f;
- disk_v *= 2.0f;
- }
- else if(disk_v < 0.75f) {
- float3 tmp = disk_N;
- disk_N = disk_T;
- disk_T = tmp;
- pick_pdf_N = 0.25f;
- pick_pdf_T = 0.5f;
- pick_pdf_B = 0.25f;
- disk_v = (disk_v - 0.5f)*4.0f;
- }
- else {
- float3 tmp = disk_N;
- disk_N = disk_B;
- disk_B = tmp;
- pick_pdf_N = 0.25f;
- pick_pdf_T = 0.25f;
- pick_pdf_B = 0.5f;
- disk_v = (disk_v - 0.75f)*4.0f;
- }
-
- /* sample point on disk */
- float phi = M_2PI_F * disk_v;
- float disk_height, disk_r;
-
- bssrdf_sample(sc, disk_u, &disk_r, &disk_height);
-
- float3 disk_P = (disk_r*cosf(phi)) * disk_T + (disk_r*sinf(phi)) * disk_B;
-
- /* create ray */
+ /* pick random axis in local frame and point on disk */
+ float3 disk_N, disk_T, disk_B;
+ float pick_pdf_N, pick_pdf_T, pick_pdf_B;
+
+ disk_N = sd->Ng;
+ make_orthonormals(disk_N, &disk_T, &disk_B);
+
+ if (disk_v < 0.5f) {
+ pick_pdf_N = 0.5f;
+ pick_pdf_T = 0.25f;
+ pick_pdf_B = 0.25f;
+ disk_v *= 2.0f;
+ }
+ else if (disk_v < 0.75f) {
+ float3 tmp = disk_N;
+ disk_N = disk_T;
+ disk_T = tmp;
+ pick_pdf_N = 0.25f;
+ pick_pdf_T = 0.5f;
+ pick_pdf_B = 0.25f;
+ disk_v = (disk_v - 0.5f) * 4.0f;
+ }
+ else {
+ float3 tmp = disk_N;
+ disk_N = disk_B;
+ disk_B = tmp;
+ pick_pdf_N = 0.25f;
+ pick_pdf_T = 0.25f;
+ pick_pdf_B = 0.5f;
+ disk_v = (disk_v - 0.75f) * 4.0f;
+ }
+
+ /* sample point on disk */
+ float phi = M_2PI_F * disk_v;
+ float disk_height, disk_r;
+
+ bssrdf_sample(sc, disk_u, &disk_r, &disk_height);
+
+ float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B;
+
+ /* create ray */
#ifdef __SPLIT_KERNEL__
- Ray ray_object = ss_isect->ray;
- Ray *ray = &ray_object;
+ Ray ray_object = ss_isect->ray;
+ Ray *ray = &ray_object;
#else
- Ray *ray = &ss_isect->ray;
+ Ray *ray = &ss_isect->ray;
#endif
- ray->P = sd->P + disk_N*disk_height + disk_P;
- ray->D = -disk_N;
- ray->t = 2.0f*disk_height;
- ray->dP = sd->dP;
- ray->dD = differential3_zero();
- ray->time = sd->time;
-
- /* intersect with the same object. if multiple intersections are found it
- * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits */
- scene_intersect_local(kg,
- *ray,
- ss_isect,
- sd->object,
- lcg_state,
- BSSRDF_MAX_HITS);
- int num_eval_hits = min(ss_isect->num_hits, BSSRDF_MAX_HITS);
-
- for(int hit = 0; hit < num_eval_hits; hit++) {
- /* Quickly retrieve P and Ng without setting up ShaderData. */
- float3 hit_P;
- if(sd->type & PRIMITIVE_TRIANGLE) {
- hit_P = triangle_refine_local(kg,
- sd,
- &ss_isect->hits[hit],
- ray);
- }
+ ray->P = sd->P + disk_N * disk_height + disk_P;
+ ray->D = -disk_N;
+ ray->t = 2.0f * disk_height;
+ ray->dP = sd->dP;
+ ray->dD = differential3_zero();
+ ray->time = sd->time;
+
+ /* intersect with the same object. if multiple intersections are found it
+ * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits */
+ scene_intersect_local(kg, *ray, ss_isect, sd->object, lcg_state, BSSRDF_MAX_HITS);
+ int num_eval_hits = min(ss_isect->num_hits, BSSRDF_MAX_HITS);
+
+ for (int hit = 0; hit < num_eval_hits; hit++) {
+ /* Quickly retrieve P and Ng without setting up ShaderData. */
+ float3 hit_P;
+ if (sd->type & PRIMITIVE_TRIANGLE) {
+ hit_P = triangle_refine_local(kg, sd, &ss_isect->hits[hit], ray);
+ }
#ifdef __OBJECT_MOTION__
- else if(sd->type & PRIMITIVE_MOTION_TRIANGLE) {
- float3 verts[3];
- motion_triangle_vertices(
- kg,
- sd->object,
- kernel_tex_fetch(__prim_index, ss_isect->hits[hit].prim),
- sd->time,
- verts);
- hit_P = motion_triangle_refine_local(kg,
- sd,
- &ss_isect->hits[hit],
- ray,
- verts);
- }
-#endif /* __OBJECT_MOTION__ */
- else {
- ss_isect->weight[hit] = make_float3(0.0f, 0.0f, 0.0f);
- continue;
- }
-
- float3 hit_Ng = ss_isect->Ng[hit];
- if(ss_isect->hits[hit].object != OBJECT_NONE) {
- object_normal_transform(kg, sd, &hit_Ng);
- }
-
- /* Probability densities for local frame axes. */
- float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng));
- float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng));
- float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng));
-
- /* Multiple importance sample between 3 axes, power heuristic
- * found to be slightly better than balance heuristic. pdf_N
- * in the MIS weight and denominator cancelled out. */
- float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B));
- if(ss_isect->num_hits > BSSRDF_MAX_HITS) {
- w *= ss_isect->num_hits/(float)BSSRDF_MAX_HITS;
- }
-
- /* Real distance to sampled point. */
- float r = len(hit_P - sd->P);
-
- /* Evaluate profiles. */
- float3 eval = subsurface_scatter_eval(sd, sc, disk_r, r, all) * w;
-
- ss_isect->weight[hit] = eval;
- }
+ else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) {
+ float3 verts[3];
+ motion_triangle_vertices(kg,
+ sd->object,
+ kernel_tex_fetch(__prim_index, ss_isect->hits[hit].prim),
+ sd->time,
+ verts);
+ hit_P = motion_triangle_refine_local(kg, sd, &ss_isect->hits[hit], ray, verts);
+ }
+#endif /* __OBJECT_MOTION__ */
+ else {
+ ss_isect->weight[hit] = make_float3(0.0f, 0.0f, 0.0f);
+ continue;
+ }
+
+ float3 hit_Ng = ss_isect->Ng[hit];
+ if (ss_isect->hits[hit].object != OBJECT_NONE) {
+ object_normal_transform(kg, sd, &hit_Ng);
+ }
+
+ /* Probability densities for local frame axes. */
+ float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng));
+ float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng));
+ float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng));
+
+ /* Multiple importance sample between 3 axes, power heuristic
+ * found to be slightly better than balance heuristic. pdf_N
+ * in the MIS weight and denominator cancelled out. */
+ float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B));
+ if (ss_isect->num_hits > BSSRDF_MAX_HITS) {
+ w *= ss_isect->num_hits / (float)BSSRDF_MAX_HITS;
+ }
+
+ /* Real distance to sampled point. */
+ float r = len(hit_P - sd->P);
+
+ /* Evaluate profiles. */
+ float3 eval = subsurface_scatter_eval(sd, sc, disk_r, r, all) * w;
+
+ ss_isect->weight[hit] = eval;
+ }
#ifdef __SPLIT_KERNEL__
- ss_isect->ray = *ray;
+ ss_isect->ray = *ray;
#endif
- return num_eval_hits;
+ return num_eval_hits;
}
-ccl_device_noinline void subsurface_scatter_multi_setup(
- KernelGlobals *kg,
- LocalIntersection* ss_isect,
- int hit,
- ShaderData *sd,
- ccl_addr_space PathState *state,
- ClosureType type,
- float roughness)
+ccl_device_noinline void subsurface_scatter_multi_setup(KernelGlobals *kg,
+ LocalIntersection *ss_isect,
+ int hit,
+ ShaderData *sd,
+ ccl_addr_space PathState *state,
+ ClosureType type,
+ float roughness)
{
#ifdef __SPLIT_KERNEL__
- Ray ray_object = ss_isect->ray;
- Ray *ray = &ray_object;
+ Ray ray_object = ss_isect->ray;
+ Ray *ray = &ray_object;
#else
- Ray *ray = &ss_isect->ray;
+ Ray *ray = &ss_isect->ray;
#endif
- /* Workaround for AMD GPU OpenCL compiler. Most probably cache bypass issue. */
+ /* Workaround for AMD GPU OpenCL compiler. Most probably cache bypass issue. */
#if defined(__SPLIT_KERNEL__) && defined(__KERNEL_OPENCL_AMD__) && defined(__KERNEL_GPU__)
- kernel_split_params.dummy_sd_flag = sd->flag;
+ kernel_split_params.dummy_sd_flag = sd->flag;
#endif
- /* Setup new shading point. */
- shader_setup_from_subsurface(kg, sd, &ss_isect->hits[hit], ray);
+ /* Setup new shading point. */
+ shader_setup_from_subsurface(kg, sd, &ss_isect->hits[hit], ray);
- /* Optionally blur colors and bump mapping. */
- float3 weight = ss_isect->weight[hit];
- float3 N = sd->N;
- subsurface_color_bump_blur(kg, sd, state, &weight, &N);
+ /* Optionally blur colors and bump mapping. */
+ float3 weight = ss_isect->weight[hit];
+ float3 N = sd->N;
+ subsurface_color_bump_blur(kg, sd, state, &weight, &N);
- /* Setup diffuse BSDF. */
- subsurface_scatter_setup_diffuse_bsdf(kg, sd, type, roughness, weight, N);
+ /* Setup diffuse BSDF. */
+ subsurface_scatter_setup_diffuse_bsdf(kg, sd, type, roughness, weight, N);
}
/* Random walk subsurface scattering.
@@ -340,196 +318,178 @@ ccl_device_noinline void subsurface_scatter_multi_setup(
* "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)
+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);
+ /* 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;
+ *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)
+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;
+ 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);
+ 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);
+ *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);
+ /* 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)
+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. */
+ /* 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;
+ Ray ray_object = ss_isect->ray;
+ Ray *ray = &ray_object;
#else
- Ray *ray = &ss_isect->ray;
+ 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;
+ 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;
+ ss_isect->ray = *ray;
#endif
- return 1;
+ 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)
+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);
- }
+ 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