Refactor: Cycles: Extract emission component from Principled BSDF

OpenPBR can also have emission, so the compiler hint for optimizing out
other BSDFs doesn't work anymore.
Instead, we extract the function to compute emission, and share it when
evaluating BSDF and surface emission
This commit is contained in:
Weizhen Huang 2026-05-28 08:48:44 +02:00 • committed by Weizhen Huang
parent 53a0e4c271
commit ee2fe3cc52

View file

@ -89,6 +89,128 @@ ccl_device_inline int svm_node_closure_bsdf_skip(int offset, const uint type)
return offset;
}
/* Compute emission attenuated by coat and sheen for Principled BSDF, and return the weight of the
* layers after emission. */
ccl_device_inline Spectrum
principled_bsdf_emission(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodePrincipledBsdfData &data,
const float3 N,
const bool reflective_caustics,
const uint32_t path_flag,
const float mix_weight)
{
/* We're ignoring closure_weight here since it's always 1 for the Principled BSDF, so there's no
* point in setting it. */
Spectrum weight = make_spectrum(mix_weight);
/* Before any actual shader components, apply transparency. */
const float alpha = saturatef(stack_load(stack, data.alpha));
if (alpha < 1.0f) {
bsdf_transparent_setup(sd, weight * (1.0f - alpha), path_flag);
weight *= alpha;
}
/* First layer: Sheen */
const float coat_weight = fmaxf(stack_load(stack, data.coat_weight), 0.0f);
const float sheen_weight = fmaxf(stack_load(stack, data.sheen_weight), 0.0f);
if (sheen_weight > CLOSURE_WEIGHT_CUTOFF) {
const float3 sheen_tint = max(stack_load(stack, data.sheen_tint), zero_float3());
const float sheen_roughness = saturatef(stack_load(stack, data.sheen_roughness));
ccl_private SheenBsdf *bsdf = (ccl_private SheenBsdf *)bsdf_alloc(
sd, sizeof(SheenBsdf), sheen_weight * rgb_to_spectrum(sheen_tint) * weight);
if (bsdf) {
const float3 coat_normal = safe_normalize_fallback(
stack_load_float3_default(stack, data.coat_normal_offset, N), sd->N);
bsdf->N = safe_normalize(mix(N, coat_normal, saturatef(coat_weight)));
bsdf->roughness = sheen_roughness;
/* setup bsdf */
const int sheen_flag = bsdf_sheen_setup(kg, sd, bsdf);
if (sheen_flag) {
sd->flag |= sheen_flag;
/* Attenuate lower layers */
const Spectrum albedo = bsdf_albedo(
kg, sd, (ccl_private ShaderClosure *)bsdf, true, false);
weight = closure_layering_weight(albedo, weight);
}
}
}
/* Second layer: Coat */
if (coat_weight > CLOSURE_WEIGHT_CUTOFF) {
const float coat_roughness = saturatef(stack_load(stack, data.coat_roughness));
const float coat_ior = fmaxf(stack_load(stack, data.coat_ior), 1.0f);
const float3 coat_tint = max(stack_load(stack, data.coat_tint), zero_float3());
const float3 coat_normal = safe_normalize_fallback(
stack_load_float3_default(stack, data.coat_normal_offset, N), sd->N);
const float3 valid_coat_normal = maybe_ensure_valid_specular_reflection(sd, coat_normal);
if (reflective_caustics) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), coat_weight * weight);
if (bsdf) {
bsdf->N = valid_coat_normal;
bsdf->T = zero_float3();
bsdf->ior = coat_ior;
bsdf->alpha_x = bsdf->alpha_y = sqr(coat_roughness);
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
bsdf_microfacet_setup_fresnel_dielectric(kg, bsdf, sd->wi);
/* Attenuate lower layers */
const Spectrum albedo = bsdf_albedo(
kg, sd, (ccl_private ShaderClosure *)bsdf, true, false);
weight = closure_layering_weight(albedo, weight);
}
}
if (!isequal(coat_tint, one_float3())) {
/* Tint is normalized to perpendicular incidence.
* Therefore, if we define the coat thickness as length 1, the length along the ray is
* t = sqrt(1+tan^2(angle(N, I))) = sqrt(1+tan^2(acos(dotNI))) = 1 / dotNI.
* From Beer's law, we have T = exp(-sigma_e * t).
* Therefore, tint = exp(-sigma_e * 1) (per def.), so -sigma_e = log(tint).
* From this, T = exp(log(tint) * t) = exp(log(tint)) ^ t = tint ^ t;
*
* Note that this is only an approximation - it assumes that the outgoing ray follows the
* same angle, and that there aren't multiple internal bounces. In particular, things that
* could be improved:
* - For transmissive materials, there should not be an outgoing path at all if the path is
* transmitted.
* - For rough materials, we could blend towards a view-independent average path length
* (e.g. 2 for diffuse reflection) for the outgoing direction.
* However, there's also an argument to be made for keeping parameters independent of each
* other for more intuitive control, in particular main roughness not affecting the coat.
*/
const float cosNI = dot(sd->wi, valid_coat_normal);
/* Refract incoming direction into coat material.
* TIR is no concern here since we're always coming from the outside. */
const float cosNT = sqrtf(1.0f - sqr(1.0f / coat_ior) * (1 - sqr(cosNI)));
const float optical_depth = 1.0f / cosNT;
weight *= mix(one_spectrum(), power(rgb_to_spectrum(coat_tint), optical_depth), coat_weight);
}
}
/* Emission (attenuated by sheen and coat) */
const float3 emission = rgb_to_spectrum(stack_load(stack, data.emission_color)) *
stack_load(stack, data.emission_strength);
if (!is_zero(emission)) {
emission_setup(sd, rgb_to_spectrum(emission) * weight);
}
return weight;
}
template<uint node_feature_mask, ShaderType shader_type>
#ifndef __KERNEL_ONEAPI__
ccl_device_noinline
@ -125,8 +247,22 @@ ccl_device
return svm_node_closure_bsdf_skip(offset, type);
}
/* Help the compiler to optimize out other BSDFs. */
type = CLOSURE_BSDF_PRINCIPLED_ID;
const ccl_global SVMNodePrincipledBsdfData &data = svm_node_get<SVMNodePrincipledBsdfData>(
kg, &offset);
float3 N = stack_load_float3_default(stack, data.normal_offset, sd->N);
N = safe_normalize_fallback(N, sd->N);
#ifdef __CAUSTICS_TRICKS__
const bool reflective_caustics = (kernel_data.integrator.caustics_reflective ||
(ray_visibility & PATH_RAY_VISIBILITY_DIFFUSE) == 0);
#else
const bool reflective_caustics = true;
#endif
principled_bsdf_emission(kg, sd, stack, data, N, reflective_caustics, path_flag, mix_weight);
return offset;
}
else {
return svm_node_closure_bsdf_skip(offset, type);
@ -140,10 +276,6 @@ ccl_device
float3 N = stack_load_float3_default(stack, data.normal_offset, sd->N);
N = safe_normalize_fallback(N, sd->N);
/* We're ignoring closure_weight here since it's always 1 for the Principled BSDF, so there's
* no point in setting it. */
Spectrum weight = make_spectrum(mix_weight);
#ifdef __CAUSTICS_TRICKS__
const bool reflective_caustics = (kernel_data.integrator.caustics_reflective ||
(ray_visibility & PATH_RAY_VISIBILITY_DIFFUSE) == 0);
@ -151,343 +283,237 @@ ccl_device
const bool reflective_caustics = true;
#endif
/* Before any actual shader components, apply transparency. */
const float alpha = saturatef(stack_load(stack, data.alpha));
if (alpha < 1.0f) {
bsdf_transparent_setup(sd, weight * (1.0f - alpha), path_flag);
weight *= alpha;
}
Spectrum weight = principled_bsdf_emission(
kg, sd, stack, data, N, reflective_caustics, path_flag, mix_weight);
/* First layer: Sheen */
const float coat_weight = fmaxf(stack_load(stack, data.coat_weight), 0.0f);
const float sheen_weight = fmaxf(stack_load(stack, data.sheen_weight), 0.0f);
const Spectrum base_color = rgb_to_spectrum(
max(stack_load(stack, data.base_color), zero_float3()));
const Spectrum clamped_base_color = min(base_color, one_spectrum());
const float ior = fmaxf(stack_load(stack, data.ior), 1e-5f);
const float roughness = saturatef(stack_load(stack, data.roughness));
const float3 valid_reflection_N = maybe_ensure_valid_specular_reflection(sd, N);
const float anisotropic = saturatef(stack_load(stack, data.anisotropic));
if (sheen_weight > CLOSURE_WEIGHT_CUTOFF) {
const float3 sheen_tint = max(stack_load(stack, data.sheen_tint), zero_float3());
const float sheen_roughness = saturatef(stack_load(stack, data.sheen_roughness));
const ClosureType distribution = data.distribution;
const Spectrum specular_tint = rgb_to_spectrum(
max(stack_load(stack, data.specular_tint), zero_float3()));
const float thinfilm_thickness = stack_load(stack, data.thin_film_thickness);
const float thinfilm_ior = (thinfilm_thickness > THINFILM_THICKNESS_CUTOFF) ?
fmaxf(stack_load(stack, data.thin_film_ior), 1e-5f) :
0.0f;
ccl_private SheenBsdf *bsdf = (ccl_private SheenBsdf *)bsdf_alloc(
sd, sizeof(SheenBsdf), sheen_weight * rgb_to_spectrum(sheen_tint) * weight);
if (bsdf) {
const float3 coat_normal = safe_normalize_fallback(
stack_load_float3_default(stack, data.coat_normal_offset, N), sd->N);
bsdf->N = safe_normalize(mix(N, coat_normal, saturatef(coat_weight)));
bsdf->roughness = sheen_roughness;
/* setup bsdf */
const int sheen_flag = bsdf_sheen_setup(kg, sd, bsdf);
if (sheen_flag) {
sd->flag |= sheen_flag;
/* Attenuate lower layers */
const Spectrum albedo = bsdf_albedo(
kg, sd, (ccl_private ShaderClosure *)bsdf, true, false);
weight = closure_layering_weight(albedo, weight);
}
float alpha_x = sqr(roughness);
float alpha_y = sqr(roughness);
float3 T = zero_float3();
if (anisotropic > 0.0f && stack_valid(data.tangent_offset)) {
T = stack_load_float3(stack, data.tangent_offset);
const float aspect = sqrtf(1.0f - anisotropic * 0.9f);
alpha_x /= aspect;
alpha_y *= aspect;
const float anisotropic_rotation = stack_load(stack, data.anisotropic_rotation);
if (anisotropic_rotation != 0.0f) {
T = rotate_around_axis(T, N, anisotropic_rotation * M_2PI_F);
}
}
/* Second layer: Coat */
if (coat_weight > CLOSURE_WEIGHT_CUTOFF) {
const float coat_roughness = saturatef(stack_load(stack, data.coat_roughness));
const float coat_ior = fmaxf(stack_load(stack, data.coat_ior), 1.0f);
const float3 coat_tint = max(stack_load(stack, data.coat_tint), zero_float3());
const float3 coat_normal = safe_normalize_fallback(
stack_load_float3_default(stack, data.coat_normal_offset, N), sd->N);
const float3 valid_coat_normal = maybe_ensure_valid_specular_reflection(sd, coat_normal);
/* Metallic component */
const float metallic = saturatef(stack_load(stack, data.metallic));
if (metallic > CLOSURE_WEIGHT_CUTOFF) {
if (reflective_caustics) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), coat_weight * weight);
if (bsdf) {
bsdf->N = valid_coat_normal;
bsdf->T = zero_float3();
bsdf->ior = coat_ior;
bsdf->alpha_x = bsdf->alpha_y = sqr(coat_roughness);
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
bsdf_microfacet_setup_fresnel_dielectric(kg, bsdf, sd->wi);
/* Attenuate lower layers */
const Spectrum albedo = bsdf_albedo(
kg, sd, (ccl_private ShaderClosure *)bsdf, true, false);
weight = closure_layering_weight(albedo, weight);
}
}
if (!isequal(coat_tint, one_float3())) {
/* Tint is normalized to perpendicular incidence.
* Therefore, if we define the coat thickness as length 1, the length along the ray is
* t = sqrt(1+tan^2(angle(N, I))) = sqrt(1+tan^2(acos(dotNI))) = 1 / dotNI.
* From Beer's law, we have T = exp(-sigma_e * t).
* Therefore, tint = exp(-sigma_e * 1) (per def.), so -sigma_e = log(tint).
* From this, T = exp(log(tint) * t) = exp(log(tint)) ^ t = tint ^ t;
*
* Note that this is only an approximation - it assumes that the outgoing ray
* follows the same angle, and that there aren't multiple internal bounces.
* In particular, things that could be improved:
* - For transmissive materials, there should not be an outgoing path at all if the path
* is transmitted.
* - For rough materials, we could blend towards a view-independent average path length
* (e.g. 2 for diffuse reflection) for the outgoing direction.
* However, there's also an argument to be made for keeping parameters independent of
* each other for more intuitive control, in particular main roughness not affecting the
* coat.
*/
const float cosNI = dot(sd->wi, valid_coat_normal);
/* Refract incoming direction into coat material.
* TIR is no concern here since we're always coming from the outside. */
const float cosNT = sqrtf(1.0f - sqr(1.0f / coat_ior) * (1 - sqr(cosNI)));
const float optical_depth = 1.0f / cosNT;
weight *= mix(
one_spectrum(), power(rgb_to_spectrum(coat_tint), optical_depth), coat_weight);
}
}
/* Emission (attenuated by sheen and coat) */
const float3 emission = rgb_to_spectrum(stack_load(stack, data.emission_color)) *
stack_load(stack, data.emission_strength);
if (!is_zero(emission)) {
emission_setup(sd, rgb_to_spectrum(emission) * weight);
}
IF_KERNEL_NODES_FEATURE(BSDF)
{
const Spectrum base_color = rgb_to_spectrum(
max(stack_load(stack, data.base_color), zero_float3()));
const Spectrum clamped_base_color = min(base_color, one_spectrum());
const float ior = fmaxf(stack_load(stack, data.ior), 1e-5f);
const float roughness = saturatef(stack_load(stack, data.roughness));
const float3 valid_reflection_N = maybe_ensure_valid_specular_reflection(sd, N);
const float anisotropic = saturatef(stack_load(stack, data.anisotropic));
const ClosureType distribution = data.distribution;
const Spectrum specular_tint = rgb_to_spectrum(
max(stack_load(stack, data.specular_tint), zero_float3()));
const float thinfilm_thickness = stack_load(stack, data.thin_film_thickness);
const float thinfilm_ior = (thinfilm_thickness > THINFILM_THICKNESS_CUTOFF) ?
fmaxf(stack_load(stack, data.thin_film_ior), 1e-5f) :
0.0f;
float alpha_x = sqr(roughness);
float alpha_y = sqr(roughness);
float3 T = zero_float3();
if (anisotropic > 0.0f && stack_valid(data.tangent_offset)) {
T = stack_load_float3(stack, data.tangent_offset);
const float aspect = sqrtf(1.0f - anisotropic * 0.9f);
alpha_x /= aspect;
alpha_y *= aspect;
const float anisotropic_rotation = stack_load(stack, data.anisotropic_rotation);
if (anisotropic_rotation != 0.0f) {
T = rotate_around_axis(T, N, anisotropic_rotation * M_2PI_F);
}
}
/* Metallic component */
const float metallic = saturatef(stack_load(stack, data.metallic));
if (metallic > CLOSURE_WEIGHT_CUTOFF) {
if (reflective_caustics) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), metallic * weight);
ccl_private FresnelF82Tint *fresnel =
(bsdf != nullptr) ?
(ccl_private FresnelF82Tint *)closure_alloc_extra(sd, sizeof(FresnelF82Tint)) :
nullptr;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->ior = 1.0f;
bsdf->T = T;
bsdf->alpha_x = alpha_x;
bsdf->alpha_y = alpha_y;
fresnel->f0 = clamped_base_color;
const Spectrum f82 = min(specular_tint, one_spectrum());
fresnel->thin_film.thickness = thinfilm_thickness;
fresnel->thin_film.ior = thinfilm_ior;
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
const bool is_multiggx = (distribution ==
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_f82_tint(kg, bsdf, sd->wi, fresnel, f82, is_multiggx);
}
}
/* Attenuate other components */
weight *= (1.0f - metallic);
}
#ifdef __CAUSTICS_TRICKS__
const bool refractive_caustics = (kernel_data.integrator.caustics_refractive ||
(ray_visibility & PATH_RAY_VISIBILITY_DIFFUSE) == 0);
#else
const bool refractive_caustics = true;
#endif
const bool thin_wall = stack_load(stack, data.thin_wall);
/* Transmission component */
const float transmission_weight = saturatef(stack_load(stack, data.transmission_weight));
if (transmission_weight > CLOSURE_WEIGHT_CUTOFF) {
if (reflective_caustics || refractive_caustics) {
const bool backfacing = !thin_wall && (sd->flag & SD_BACKFACING);
const FresnelThinFilm thinfilm = {thinfilm_thickness,
backfacing ? thinfilm_ior / ior : thinfilm_ior};
if (thin_wall) {
Spectrum reflectance, transmittance;
bsdf_thin_glass_setup(kg,
sd,
reflective_caustics,
refractive_caustics,
specular_tint,
clamped_base_color,
transmission_weight * weight,
valid_reflection_N,
sqr(roughness),
ior,
thinfilm,
&reflectance,
&transmittance);
}
else {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), transmission_weight * weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != nullptr) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
nullptr;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->T = zero_float3();
bsdf->alpha_x = bsdf->alpha_y = sqr(roughness);
bsdf->ior = backfacing ? 1.0f / ior : ior;
*fresnel = generalized_schlick_setup(ior,
reflective_caustics,
refractive_caustics,
specular_tint,
sqrt(clamped_base_color),
thinfilm);
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
const bool is_multiggx = (distribution ==
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_generalized_schlick(
kg, bsdf, sd->wi, fresnel, is_multiggx);
}
}
}
/* Attenuate other components */
weight *= (1.0f - transmission_weight);
}
/* Apply IOR adjustment */
const float specular_ior_level = max(stack_load(stack, data.specular_ior_level), 0.0f);
float eta = ior;
float f0 = F0_from_ior(eta);
if (specular_ior_level != 0.5f) {
f0 *= 2.0f * specular_ior_level;
eta = ior_from_F0(f0);
if (ior < 1.0f) {
eta = 1.0f / eta;
}
}
/* Specular component */
if (reflective_caustics && (eta != 1.0f || thinfilm_thickness > 0.1f)) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != nullptr) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
nullptr;
sd, sizeof(MicrofacetBsdf), metallic * weight);
ccl_private FresnelF82Tint *fresnel =
(bsdf != nullptr) ?
(ccl_private FresnelF82Tint *)closure_alloc_extra(sd, sizeof(FresnelF82Tint)) :
nullptr;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->ior = eta;
bsdf->ior = 1.0f;
bsdf->T = T;
bsdf->alpha_x = alpha_x;
bsdf->alpha_y = alpha_y;
fresnel->f0 = f0 * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -eta;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = zero_spectrum();
fresnel->f0 = clamped_base_color;
const Spectrum f82 = min(specular_tint, one_spectrum());
fresnel->thin_film.thickness = thinfilm_thickness;
fresnel->thin_film.ior = thinfilm_ior;
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_generalized_schlick(
kg, bsdf, sd->wi, fresnel, is_multiggx);
/* Attenuate lower layers */
const Spectrum albedo = bsdf_albedo(
kg, sd, (ccl_private ShaderClosure *)bsdf, true, false);
weight = closure_layering_weight(albedo, weight);
bsdf_microfacet_setup_fresnel_f82_tint(kg, bsdf, sd->wi, fresnel, f82, is_multiggx);
}
}
/* Attenuate other components */
weight *= (1.0f - metallic);
}
#ifdef __CAUSTICS_TRICKS__
const bool refractive_caustics = (kernel_data.integrator.caustics_refractive ||
(ray_visibility & PATH_RAY_VISIBILITY_DIFFUSE) == 0);
#else
const bool refractive_caustics = true;
#endif
const bool thin_wall = stack_load(stack, data.thin_wall);
/* Transmission component */
const float transmission_weight = saturatef(stack_load(stack, data.transmission_weight));
if (transmission_weight > CLOSURE_WEIGHT_CUTOFF) {
if (reflective_caustics || refractive_caustics) {
const bool backfacing = !thin_wall && (sd->flag & SD_BACKFACING);
const FresnelThinFilm thinfilm = {thinfilm_thickness,
backfacing ? thinfilm_ior / ior : thinfilm_ior};
/* Diffuse/Subsurface component */
#ifdef __SUBSURFACE__
const float subsurface_weight = saturatef(stack_load(stack, data.subsurface_weight));
if (subsurface_weight > CLOSURE_WEIGHT_CUTOFF) {
const float anisotropy = stack_load(stack, data.subsurface_anisotropy);
const Spectrum closure_weight = clamped_base_color * subsurface_weight * weight;
if (thin_wall) {
const float diffuse_roughness = saturatef(stack_load(stack, data.diffuse_roughness));
bsdf_thin_subsurface_setup(
sd, N, closure_weight, anisotropy, diffuse_roughness, clamped_base_color);
Spectrum reflectance, transmittance;
bsdf_thin_glass_setup(kg,
sd,
reflective_caustics,
refractive_caustics,
specular_tint,
clamped_base_color,
transmission_weight * weight,
valid_reflection_N,
sqr(roughness),
ior,
thinfilm,
&reflectance,
&transmittance);
}
else {
const ClosureType subsurface_method = data.subsurface_method;
ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, closure_weight);
if (bssrdf) {
const float3 subsurface_radius = stack_load(stack, data.subsurface_radius);
const float subsurface_scale = stack_load(stack, data.subsurface_scale);
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), transmission_weight * weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != nullptr) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
nullptr;
bssrdf->radius = rgb_to_spectrum(
max(subsurface_radius * subsurface_scale, zero_float3()));
bssrdf->albedo = clamped_base_color;
bssrdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bssrdf->alpha = sqr(roughness);
/* IOR is clamped to [1.01..3.8] inside bssrdf_setup */
bssrdf->ior = eta;
/* Anisotropy is clamped to a valid range inside bssrdf_setup. */
bssrdf->anisotropy = anisotropy;
if (subsurface_method == CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID) {
bssrdf->ior = stack_load(stack, data.subsurface_ior);
}
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->T = zero_float3();
bsdf->alpha_x = bsdf->alpha_y = sqr(roughness);
bsdf->ior = backfacing ? 1.0f / ior : ior;
*fresnel = generalized_schlick_setup(ior,
reflective_caustics,
refractive_caustics,
specular_tint,
sqrt(clamped_base_color),
thinfilm);
/* setup bsdf */
sd->flag |= bssrdf_setup(sd, bssrdf, path_flag, subsurface_method);
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
const bool is_multiggx = (distribution ==
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_generalized_schlick(
kg, bsdf, sd->wi, fresnel, is_multiggx);
}
}
}
#else
const float subsurface_weight = 0.0f;
#endif
/* Attenuate other components */
weight *= (1.0f - transmission_weight);
}
const float diffuse_roughness = saturatef(stack_load(stack, data.diffuse_roughness));
const Spectrum diffuse_weight = base_color * (1.0f - subsurface_weight) * weight;
if (diffuse_roughness_is_almost_zero(diffuse_roughness)) {
bsdf_diffuse_setup(sd, N, diffuse_weight);
/* Apply IOR adjustment */
const float specular_ior_level = max(stack_load(stack, data.specular_ior_level), 0.0f);
float eta = ior;
float f0 = F0_from_ior(eta);
if (specular_ior_level != 0.5f) {
f0 *= 2.0f * specular_ior_level;
eta = ior_from_F0(f0);
if (ior < 1.0f) {
eta = 1.0f / eta;
}
}
/* Specular component */
if (reflective_caustics && (eta != 1.0f || thinfilm_thickness > 0.1f)) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != nullptr) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
nullptr;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->ior = eta;
bsdf->T = T;
bsdf->alpha_x = alpha_x;
bsdf->alpha_y = alpha_y;
fresnel->f0 = f0 * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -eta;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = zero_spectrum();
fresnel->thin_film.thickness = thinfilm_thickness;
fresnel->thin_film.ior = thinfilm_ior;
/* setup bsdf */
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
const bool is_multiggx = (distribution == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
bsdf_microfacet_setup_fresnel_generalized_schlick(
kg, bsdf, sd->wi, fresnel, is_multiggx);
/* Attenuate lower layers */
const Spectrum albedo = bsdf_albedo(
kg, sd, (ccl_private ShaderClosure *)bsdf, true, false);
weight = closure_layering_weight(albedo, weight);
}
}
/* Diffuse/Subsurface component */
#ifdef __SUBSURFACE__
const float subsurface_weight = saturatef(stack_load(stack, data.subsurface_weight));
if (subsurface_weight > CLOSURE_WEIGHT_CUTOFF) {
const float anisotropy = stack_load(stack, data.subsurface_anisotropy);
const Spectrum closure_weight = clamped_base_color * subsurface_weight * weight;
if (thin_wall) {
const float diffuse_roughness = saturatef(stack_load(stack, data.diffuse_roughness));
bsdf_thin_subsurface_setup(
sd, N, closure_weight, anisotropy, diffuse_roughness, clamped_base_color);
}
else {
bsdf_oren_nayar_setup(sd, N, diffuse_weight, diffuse_roughness, base_color);
const ClosureType subsurface_method = data.subsurface_method;
ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, closure_weight);
if (bssrdf) {
const float3 subsurface_radius = stack_load(stack, data.subsurface_radius);
const float subsurface_scale = stack_load(stack, data.subsurface_scale);
bssrdf->radius = rgb_to_spectrum(
max(subsurface_radius * subsurface_scale, zero_float3()));
bssrdf->albedo = clamped_base_color;
bssrdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bssrdf->alpha = sqr(roughness);
/* IOR is clamped to [1.01..3.8] inside bssrdf_setup */
bssrdf->ior = eta;
/* Anisotropy is clamped to a valid range inside bssrdf_setup. */
bssrdf->anisotropy = anisotropy;
if (subsurface_method == CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID) {
bssrdf->ior = stack_load(stack, data.subsurface_ior);
}
/* setup bsdf */
sd->flag |= bssrdf_setup(sd, bssrdf, path_flag, subsurface_method);
}
}
}
#else
const float subsurface_weight = 0.0f;
#endif
const float diffuse_roughness = saturatef(stack_load(stack, data.diffuse_roughness));
const Spectrum diffuse_weight = base_color * (1.0f - subsurface_weight) * weight;
if (diffuse_roughness_is_almost_zero(diffuse_roughness)) {
bsdf_diffuse_setup(sd, N, diffuse_weight);
}
else {
bsdf_oren_nayar_setup(sd, N, diffuse_weight, diffuse_roughness, base_color);
}
break;
}