For curiosity
you can find the source code for the closure in ..\intern\cycles\kernel\svm\closure.h
here, and take a look at how 'BSDF_PRINCIPLED' is calculated:
case CLOSURE_BSDF_PRINCIPLED_ID: {
uint specular_ior_level_offset, roughness_offset, specular_tint_offset, anisotropic_offset,
sheen_weight_offset, sheen_tint_offset, sheen_roughness_offset, coat_weight_offset,
coat_roughness_offset, coat_ior_offset, eta_offset, transmission_weight_offset,
anisotropic_rotation_offset, coat_tint_offset, coat_normal_offset, dummy, alpha_offset,
emission_strength_offset, emission_offset, unused;
uint4 data_node2 = read_node(kg, &offset);
float3 T = stack_load_float3(stack, data_node.y);
svm_unpack_node_uchar4(data_node.z,
&specular_ior_level_offset,
&roughness_offset,
&specular_tint_offset,
&anisotropic_offset);
svm_unpack_node_uchar4(
data_node.w, &sheen_weight_offset, &sheen_tint_offset, &sheen_roughness_offset, &unused);
svm_unpack_node_uchar4(data_node2.x,
&eta_offset,
&transmission_weight_offset,
&anisotropic_rotation_offset,
&coat_normal_offset);
svm_unpack_node_uchar4(data_node2.w,
&coat_weight_offset,
&coat_roughness_offset,
&coat_ior_offset,
&coat_tint_offset);
// get Disney principled parameters
float metallic = saturatef(param1);
float subsurface_weight = saturatef(param2);
float specular_ior_level = fmaxf(stack_load_float(stack, specular_ior_level_offset), 0.0f);
float roughness = saturatef(stack_load_float(stack, roughness_offset));
Spectrum specular_tint = rgb_to_spectrum(
max(stack_load_float3(stack, specular_tint_offset), zero_float3()));
float anisotropic = saturatef(stack_load_float(stack, anisotropic_offset));
float sheen_weight = saturatef(stack_load_float(stack, sheen_weight_offset));
float3 sheen_tint = stack_load_float3(stack, sheen_tint_offset);
float sheen_roughness = saturatef(stack_load_float(stack, sheen_roughness_offset));
float coat_weight = saturatef(stack_load_float(stack, coat_weight_offset));
float coat_roughness = saturatef(stack_load_float(stack, coat_roughness_offset));
float coat_ior = fmaxf(stack_load_float(stack, coat_ior_offset), 1.0f);
float3 coat_tint = stack_load_float3(stack, coat_tint_offset);
float transmission_weight = saturatef(stack_load_float(stack, transmission_weight_offset));
float anisotropic_rotation = stack_load_float(stack, anisotropic_rotation_offset);
float ior = fmaxf(stack_load_float(stack, eta_offset), 1e-5f);
ClosureType distribution = (ClosureType)data_node2.y;
ClosureType subsurface_method = (ClosureType)data_node2.z;
float3 valid_reflection_N = maybe_ensure_valid_specular_reflection(sd, N);
float3 coat_normal = stack_valid(coat_normal_offset) ?
stack_load_float3(stack, coat_normal_offset) :
sd->N;
// get the base color
uint4 data_base_color = read_node(kg, &offset);
float3 base_color = stack_valid(data_base_color.x) ?
stack_load_float3(stack, data_base_color.x) :
make_float3(__uint_as_float(data_base_color.y),
__uint_as_float(data_base_color.z),
__uint_as_float(data_base_color.w));
// get the subsurface scattering data
uint4 data_subsurf = read_node(kg, &offset);
uint4 data_alpha_emission = read_node(kg, &offset);
svm_unpack_node_uchar4(data_alpha_emission.x,
&alpha_offset,
&emission_strength_offset,
&emission_offset,
&dummy);
float alpha = stack_valid(alpha_offset) ? stack_load_float(stack, alpha_offset) :
__uint_as_float(data_alpha_emission.y);
alpha = saturatef(alpha);
float emission_strength = stack_valid(emission_strength_offset) ?
stack_load_float(stack, emission_strength_offset) :
__uint_as_float(data_alpha_emission.z);
float3 emission = stack_load_float3(stack, emission_offset) * fmaxf(emission_strength, 0.0f);
Spectrum weight = closure_weight * mix_weight;
float alpha_x = sqr(roughness), alpha_y = sqr(roughness);
if (anisotropic > 0.0f) {
float aspect = sqrtf(1.0f - anisotropic * 0.9f);
alpha_x /= aspect;
alpha_y *= aspect;
if (anisotropic_rotation != 0.0f)
T = rotate_around_axis(T, N, anisotropic_rotation * M_2PI_F);
}
#ifdef __CAUSTICS_TRICKS__
const bool reflective_caustics = (kernel_data.integrator.caustics_reflective ||
(path_flag & PATH_RAY_DIFFUSE) == 0);
const bool glass_caustics = (kernel_data.integrator.caustics_reflective ||
kernel_data.integrator.caustics_refractive ||
(path_flag & PATH_RAY_DIFFUSE) == 0);
#else
const bool reflective_caustics = true;
const bool glass_caustics = true;
#endif
/* Before any actual shader components, apply transparency. */
if (alpha < 1.0f) {
bsdf_transparent_setup(sd, weight * (1.0f - alpha), path_flag);
weight *= alpha;
}
/* First layer: Sheen */
if (sheen_weight > CLOSURE_WEIGHT_CUTOFF) {
ccl_private SheenBsdf *bsdf = (ccl_private SheenBsdf *)bsdf_alloc(
sd, sizeof(SheenBsdf), sheen_weight * rgb_to_spectrum(sheen_tint) * weight);
if (bsdf) {
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 */
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) {
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 = 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);
/* Attenuate lower layers */
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.
*/
float cosNI = dot(sd->wi, coat_normal);
/* Refract incoming direction into coat material.
* TIR is no concern here since we're always coming from the outside. */
float cosNT = sqrtf(1.0f - sqr(1.0f / coat_ior) * (1 - sqr(cosNI)));
float optical_depth = 1.0f / cosNT;
weight *= power(rgb_to_spectrum(coat_tint), coat_weight * optical_depth);
}
}
/* Emission (attenuated by sheen and coat) */
if (!is_zero(emission)) {
emission_setup(sd, rgb_to_spectrum(emission) * weight);
}
/* Metallic component */
if (reflective_caustics && metallic > CLOSURE_WEIGHT_CUTOFF) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), metallic * weight);
ccl_private FresnelF82Tint *fresnel =
(bsdf != NULL) ?
(ccl_private FresnelF82Tint *)closure_alloc_extra(sd, sizeof(FresnelF82Tint)) :
NULL;
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 = rgb_to_spectrum(base_color);
const Spectrum f82 = specular_tint;
/* 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, fresnel, f82, is_multiggx);
/* Attenuate other components */
weight *= (1.0f - metallic);
}
}
/* Transmission component */
if (glass_caustics && transmission_weight > CLOSURE_WEIGHT_CUTOFF) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), transmission_weight * weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
if (bsdf && fresnel) {
bsdf->N = valid_reflection_N;
bsdf->T = zero_float3();
bsdf->alpha_x = bsdf->alpha_y = sqr(roughness);
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / ior : ior;
fresnel->f0 = make_float3(F0_from_ior(ior)) * specular_tint;
fresnel->f90 = one_spectrum();
fresnel->exponent = -ior;
fresnel->reflection_tint = one_spectrum();
fresnel->transmission_tint = sqrt(rgb_to_spectrum(base_color));
/* 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, fresnel, is_multiggx);
/* Attenuate other components */
weight *= (1.0f - transmission_weight);
}
}
/* Apply IOR adjustment */
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) {
ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc(
sd, sizeof(MicrofacetBsdf), weight);
ccl_private FresnelGeneralizedSchlick *fresnel =
(bsdf != NULL) ? (ccl_private FresnelGeneralizedSchlick *)closure_alloc_extra(
sd, sizeof(FresnelGeneralizedSchlick)) :
NULL;
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();
/* 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, fresnel, is_multiggx);
/* Attenuate lower layers */
Spectrum albedo = bsdf_albedo(kg, sd, (ccl_private ShaderClosure *)bsdf, true, false);
weight = closure_layering_weight(albedo, weight);
}
}
/* Diffuse/Subsurface component */
#ifdef __SUBSURFACE__
ccl_private Bssrdf *bssrdf = bssrdf_alloc(
sd, rgb_to_spectrum(base_color) * subsurface_weight * weight);
if (bssrdf) {
float3 subsurface_radius = stack_load_float3(stack, data_subsurf.y);
float subsurface_scale = stack_load_float(stack, data_subsurf.z);
bssrdf->radius = rgb_to_spectrum(subsurface_radius * subsurface_scale);
bssrdf->albedo = rgb_to_spectrum(base_color);
bssrdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bssrdf->alpha = sqr(roughness);
bssrdf->ior = eta;
bssrdf->anisotropy = stack_load_float(stack, data_subsurf.w);
if (subsurface_method == CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID) {
bssrdf->ior = stack_load_float(stack, data_subsurf.x);
}
/* setup bsdf */
sd->flag |= bssrdf_setup(sd, bssrdf, path_flag, subsurface_method);
}
#else
subsurface_weight = 0.0f;
#endif
ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc(
sd,
sizeof(DiffuseBsdf),
rgb_to_spectrum(base_color) * (1.0f - subsurface_weight) * weight);
if (bsdf) {
bsdf->N = N;
/* setup bsdf */
sd->flag |= bsdf_diffuse_setup(bsdf);
}
break;
}
Recreating it "pixel perfect" with BSDFs will be a big headache.
I'm not sure that a large BSDF is actually made up of pieces of small BSDFs.