Shader: Only clamp undefined or unsupported inputs of Principled BSDF #112895
|
@ -36,7 +36,18 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
|
|||
normal Tangent = normalize(dPdu),
|
||||
output closure color BSDF = 0)
|
||||
{
|
||||
float CLOSURE_WEIGHT_CUTOFF = 1e-5;
|
||||
|
||||
/* Clamping to match SVM */
|
||||
float metallic = clamp(Metallic, 0.0, 1.0);
|
||||
float transmission = clamp(TransmissionWeight, 0.0, 1.0);
|
||||
float subsurface_weight = clamp(SubsurfaceWeight, 0.0, 1.0);
|
||||
color specular_tint = max(SpecularTint, color(0.0));
|
||||
float coat_weight = max(CoatWeight, 0.0);
|
||||
color coat_tint = max(CoatTint, color(0.0));
|
||||
float sheen_weight = max(SheenWeight, 0.0);
|
||||
color base_color = max(BaseColor, color(0.0));
|
||||
color clamped_base_color = min(base_color, color(1.0));
|
||||
|
||||
float r2 = clamp(Roughness, 0.0, 1.0);
|
||||
r2 = r2 * r2;
|
||||
|
@ -53,8 +64,8 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
|
|||
T = rotate(T, AnisotropicRotation * M_2PI, point(0.0, 0.0, 0.0), Normal);
|
||||
}
|
||||
|
||||
if (Metallic < 1.0 && TransmissionWeight < 1.0) {
|
||||
float eta = IOR;
|
||||
if (metallic < 1.0 && TransmissionWeight < 1.0) {
|
||||
float eta = max(IOR, 1e-5);
|
||||
float f0 = F0_from_ior(eta);
|
||||
if (SpecularIORLevel != 0.5) {
|
||||
f0 *= 2.0 * max(SpecularIORLevel, 0.0);
|
||||
|
@ -64,22 +75,21 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
|
|||
}
|
||||
}
|
||||
|
||||
BSDF = BaseColor * diffuse(Normal);
|
||||
if (SubsurfaceWeight > 1e-5) {
|
||||
float subsurface_weight = min(SubsurfaceWeight, 1.0);
|
||||
vector radius = SubsurfaceScale * SubsurfaceRadius;
|
||||
BSDF = base_color * diffuse(Normal);
|
||||
if (subsurface_weight > CLOSURE_WEIGHT_CUTOFF) {
|
||||
vector radius = max(SubsurfaceScale * SubsurfaceRadius, vector(0.0));
|
||||
float subsurface_ior = (subsurface_method == "random_walk_skin") ? SubsurfaceIOR : eta;
|
||||
closure color SubsurfBSDF = bssrdf(subsurface_method,
|
||||
Normal,
|
||||
Alaska marked this conversation as resolved
Outdated
|
||||
SubsurfaceScale * SubsurfaceRadius,
|
||||
BaseColor,
|
||||
radius,
|
||||
clamped_base_color,
|
||||
"roughness",
|
||||
r2,
|
||||
"ior",
|
||||
subsurface_ior,
|
||||
"anisotropy",
|
||||
SubsurfaceAnisotropy);
|
||||
BSDF = mix(BSDF, BaseColor * SubsurfBSDF, subsurface_weight);
|
||||
BSDF = mix(BSDF, clamped_base_color * SubsurfBSDF, subsurface_weight);
|
||||
}
|
||||
|
||||
if (eta != 1.0) {
|
||||
|
@ -95,38 +105,45 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
|
|||
}
|
||||
|
||||
closure color TransmissionBSDF = 0;
|
||||
if (Metallic < 1.0 && TransmissionWeight > 0.0) {
|
||||
if (metallic < 1.0 && TransmissionWeight > CLOSURE_WEIGHT_CUTOFF) {
|
||||
float eta = max(IOR, 1e-5);
|
||||
eta = backfacing() ? 1.0 / eta : eta;
|
||||
|
||||
color F0 = F0_from_ior(eta) * specular_tint;
|
||||
color F90 = color(1.0);
|
||||
|
||||
TransmissionBSDF = generalized_schlick_bsdf(
|
||||
Normal, vector(0.0), color(1.0), sqrt(BaseColor), r2, r2, F0, F90, -eta, distribution),
|
||||
BSDF = mix(BSDF, TransmissionBSDF, clamp(TransmissionWeight, 0.0, 1.0));
|
||||
Alaska marked this conversation as resolved
Outdated
Lukas Stockner
commented
Shouldn't this stay [0..1]? Shouldn't this stay [0..1]?
Also, for code consistency, it might be nicer to introduce `float transparency` at the start of the shader and handle clamping there, like with `metallic`.
Alaska
commented
Part of the clamping comes from Part of the clamping comes from `if TransmissionWeight > CLOSURE_WEIGHT_CUTOFF`. However I have added back the explicit clamp for code clarity.
|
||||
TransmissionBSDF = generalized_schlick_bsdf(Normal,
|
||||
vector(0.0),
|
||||
color(1.0),
|
||||
sqrt(clamped_base_color),
|
||||
r2,
|
||||
r2,
|
||||
F0,
|
||||
F90,
|
||||
-eta,
|
||||
distribution),
|
||||
BSDF = mix(BSDF, TransmissionBSDF, transmission);
|
||||
}
|
||||
|
||||
closure color MetallicBSDF = 0;
|
||||
if (Metallic > 0.0) {
|
||||
color F0 = BaseColor;
|
||||
color F82 = specular_tint;
|
||||
if (metallic > CLOSURE_WEIGHT_CUTOFF) {
|
||||
color F0 = clamped_base_color;
|
||||
color F82 = min(specular_tint, color(1.0));
|
||||
MetallicBSDF = microfacet_f82_tint(distribution, Normal, T, alpha_x, alpha_y, F0, F82);
|
||||
BSDF = mix(BSDF, MetallicBSDF, clamp(Metallic, 0.0, 1.0));
|
||||
BSDF = mix(BSDF, MetallicBSDF, metallic);
|
||||
}
|
||||
|
||||
if (EmissionStrength > 0.0 && EmissionColor != color(0.0)) {
|
||||
if (EmissionStrength != 0.0 && EmissionColor != color(0.0)) {
|
||||
BSDF += EmissionStrength * EmissionColor * emission();
|
||||
}
|
||||
|
||||
if (CoatWeight > 1e-5) {
|
||||
if (coat_weight > CLOSURE_WEIGHT_CUTOFF) {
|
||||
float coat_ior = max(CoatIOR, 1.0);
|
||||
Alaska marked this conversation as resolved
Outdated
Lukas Stockner
commented
As above - having all the clamping together at the start makes it more maintainable (except for cases where the same parameter gets clamped differently depending on context). As above - having all the clamping together at the start makes it more maintainable (except for cases where the same parameter gets clamped differently depending on context).
|
||||
float coat_weight = clamp(CoatWeight, 0.0, 1.0);
|
||||
if (CoatTint != color(1.0)) {
|
||||
float coat_neta = 1.0 / coat_ior;
|
||||
float cosNI = dot(I, CoatNormal);
|
||||
float cosNT = sqrt(1.0 - coat_neta * coat_neta * (1 - cosNI * cosNI));
|
||||
BSDF *= mix(color(1.0), pow(CoatTint, 1.0 / cosNT), coat_weight);
|
||||
BSDF *= mix(color(1.0), pow(CoatTint, 1.0 / cosNT), clamp(coat_weight, 0.0, 1.0));
|
||||
}
|
||||
float coat_r2 = clamp(CoatRoughness, 0.0, 1.0);
|
||||
coat_r2 = coat_r2 * coat_r2;
|
||||
|
@ -136,10 +153,10 @@ shader node_principled_bsdf(string distribution = "multi_ggx",
|
|||
BSDF = layer(coat_weight * CoatBSDF, BSDF);
|
||||
}
|
||||
|
||||
if (SheenWeight > 1e-5) {
|
||||
normal sheen_normal = normalize(mix(Normal, CoatNormal, clamp(CoatWeight, 0.0, 1.0)));
|
||||
if (SheenWeight > CLOSURE_WEIGHT_CUTOFF) {
|
||||
normal sheen_normal = normalize(mix(Normal, CoatNormal, clamp(coat_weight, 0.0, 1.0)));
|
||||
closure color SheenBSDF = sheen(sheen_normal, clamp(SheenRoughness, 0.0, 1.0));
|
||||
BSDF = layer(clamp(SheenWeight, 0.0, 1.0) * SheenTint * SheenBSDF, BSDF);
|
||||
BSDF = layer(sheen_weight * max(SheenTint, color(0.0)) * SheenBSDF, BSDF);
|
||||
}
|
||||
|
||||
BSDF = mix(transparent(), BSDF, clamp(Alpha, 0.0, 1.0));
|
||||
|
|
|
@ -109,18 +109,18 @@ ccl_device
|
|||
// 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 specular_ior_level = max(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));
|
||||
Alaska marked this conversation as resolved
Outdated
Lukas Stockner
commented
Might as well do it explicitly here I guess. Might as well do it explicitly here I guess.
|
||||
float sheen_weight = saturatef(stack_load_float(stack, sheen_weight_offset));
|
||||
float3 sheen_tint = stack_load_float3(stack, sheen_tint_offset);
|
||||
float sheen_weight = max(stack_load_float(stack, sheen_weight_offset), 0.0f);
|
||||
float3 sheen_tint = max(stack_load_float3(stack, sheen_tint_offset), zero_float3());
|
||||
Alaska marked this conversation as resolved
Outdated
Lukas Stockner
commented
I'm not sure where? I'm not sure where?
Alaska
commented
Sheen weight is clamped to [0..infinity] with However I re-added the full clamping for code clarity. Sheen weight is clamped to [0..infinity] with `if (sheen_weight > CLOSURE_WEIGHT_CUTOFF)` later on.
However I re-added the full clamping for code clarity.
|
||||
float sheen_roughness = saturatef(stack_load_float(stack, sheen_roughness_offset));
|
||||
float coat_weight = saturatef(stack_load_float(stack, coat_weight_offset));
|
||||
float coat_weight = fmaxf(stack_load_float(stack, coat_weight_offset), 0.0f);
|
||||
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);
|
||||
float3 coat_tint = max(stack_load_float3(stack, coat_tint_offset), zero_float3());
|
||||
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);
|
||||
|
@ -140,6 +140,8 @@ ccl_device
|
|||
make_float3(__uint_as_float(data_base_color.y),
|
||||
__uint_as_float(data_base_color.z),
|
||||
__uint_as_float(data_base_color.w));
|
||||
base_color = max(base_color, zero_float3());
|
||||
const float3 clamped_base_color = min(base_color, one_float3());
|
||||
|
||||
// get the subsurface scattering data
|
||||
uint4 data_subsurf = read_node(kg, &offset);
|
||||
|
@ -157,7 +159,7 @@ ccl_device
|
|||
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);
|
||||
float3 emission = stack_load_float3(stack, emission_offset) * emission_strength;
|
||||
|
||||
Spectrum weight = closure_weight * mix_weight;
|
||||
|
||||
|
@ -283,8 +285,8 @@ ccl_device
|
|||
bsdf->alpha_x = alpha_x;
|
||||
bsdf->alpha_y = alpha_y;
|
||||
|
||||
fresnel->f0 = rgb_to_spectrum(base_color);
|
||||
const Spectrum f82 = specular_tint;
|
||||
fresnel->f0 = rgb_to_spectrum(clamped_base_color);
|
||||
const Spectrum f82 = min(specular_tint, one_spectrum());
|
||||
|
||||
/* setup bsdf */
|
||||
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
|
||||
|
@ -316,7 +318,7 @@ ccl_device
|
|||
fresnel->f90 = one_spectrum();
|
||||
fresnel->exponent = -ior;
|
||||
fresnel->reflection_tint = one_spectrum();
|
||||
fresnel->transmission_tint = sqrt(rgb_to_spectrum(base_color));
|
||||
fresnel->transmission_tint = sqrt(rgb_to_spectrum(clamped_base_color));
|
||||
|
||||
/* setup bsdf */
|
||||
sd->flag |= bsdf_microfacet_ggx_glass_setup(bsdf);
|
||||
|
@ -375,16 +377,18 @@ ccl_device
|
|||
/* Diffuse/Subsurface component */
|
||||
#ifdef __SUBSURFACE__
|
||||
ccl_private Bssrdf *bssrdf = bssrdf_alloc(
|
||||
sd, rgb_to_spectrum(base_color) * subsurface_weight * weight);
|
||||
sd, rgb_to_spectrum(clamped_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->radius = rgb_to_spectrum(max(subsurface_radius * subsurface_scale, zero_float3()));
|
||||
bssrdf->albedo = rgb_to_spectrum(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 [0.0..0.9] inside bssrdf_setup */
|
||||
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);
|
||||
|
|
|
@ -60,22 +60,36 @@ void node_bsdf_principled(vec4 base_color,
|
|||
out Closure result)
|
||||
{
|
||||
/* Match cycles. */
|
||||
metallic = clamp(metallic, 0.0, 1.0);
|
||||
roughness = clamp(roughness, 0.0, 1.0);
|
||||
metallic = saturate(metallic);
|
||||
Lukas Stockner
commented
I think this should be I think this should be `saturate(base_color)`.
|
||||
roughness = saturate(roughness);
|
||||
ior = max(ior, 1e-5);
|
||||
transmission_weight = clamp(transmission_weight, 0.0, 1.0);
|
||||
subsurface_weight = clamp(subsurface_weight, 0.0, 1.0);
|
||||
alpha = saturate(alpha);
|
||||
subsurface_weight = saturate(subsurface_weight);
|
||||
/* Not used by EEVEE */
|
||||
/* subsurface_anisotropy = clamp(subsurface_anisotropy, 0.0, 0.9); */
|
||||
/* subsurface_ior = clamp(subsurface_ior, 1.01, 3.8); */
|
||||
specular_ior_level = max(specular_ior_level, 0.0);
|
||||
specular_tint = max(specular_tint, vec4(0.0));
|
||||
/* Not used by EEVEE */
|
||||
/* anisotropic = clamp(anisotropic, 0.0, 1.0) */
|
||||
coat_weight = clamp(coat_weight, 0.0, 1.0);
|
||||
coat_roughness = clamp(coat_roughness, 0.0, 1.0);
|
||||
/* anisotropic = saturate(anisotropic); */
|
||||
transmission_weight = saturate(transmission_weight);
|
||||
coat_weight = max(coat_weight, 0.0);
|
||||
coat_roughness = saturate(coat_roughness);
|
||||
coat_ior = max(coat_ior, 1.0);
|
||||
sheen_weight = clamp(sheen_weight, 0.0, 1.0);
|
||||
sheen_roughness = clamp(sheen_roughness, 0.0, 1.0);
|
||||
emission_strength = max(emission_strength, 0.0);
|
||||
alpha = clamp(alpha, 0.0, 1.0);
|
||||
coat_tint = max(coat_tint, vec4(0.0));
|
||||
sheen_weight = max(sheen_weight, 0.0);
|
||||
sheen_roughness = saturate(sheen_roughness);
|
||||
sheen_tint = max(sheen_tint, vec4(0.0));
|
||||
|
||||
base_color = max(base_color, vec4(0.0));
|
||||
vec4 clamped_base_color = min(base_color, vec4(1.0));
|
||||
|
||||
vec4 diffuse_sss_base_color = base_color;
|
||||
if (subsurface_weight > 0.0) {
|
||||
/* Subsurface Scattering materials behave unpredictably with values greater than 1.0 in Cycles.
|
||||
* So it's clamped there and we clamp here for consistency with Cycles. */
|
||||
diffuse_sss_base_color = mix(diffuse_sss_base_color, clamped_base_color, subsurface_weight);
|
||||
}
|
||||
|
||||
N = safe_normalize(N);
|
||||
CN = safe_normalize(CN);
|
||||
|
@ -97,7 +111,7 @@ void node_bsdf_principled(vec4 base_color,
|
|||
vec3 sheen_color = sheen_weight * sheen_tint.rgb * principled_sheen(NV, sheen_roughness);
|
||||
diffuse_data.color = weight * sheen_color;
|
||||
/* Attenuate lower layers */
|
||||
weight *= (1.0 - max_v3(sheen_color));
|
||||
weight *= max((1.0 - max_v3(sheen_color)), 0.0);
|
||||
}
|
||||
else {
|
||||
diffuse_data.color = vec3(0.0);
|
||||
|
@ -114,13 +128,13 @@ void node_bsdf_principled(vec4 base_color,
|
|||
float reflectance = bsdf_lut(coat_NV, coat_data.roughness, coat_ior, false).x;
|
||||
coat_data.weight = weight * coat_weight * reflectance;
|
||||
/* Attenuate lower layers */
|
||||
weight *= (1.0 - reflectance * coat_weight);
|
||||
weight *= max((1.0 - reflectance * coat_weight), 0.0);
|
||||
|
||||
if (!all(equal(coat_tint.rgb, vec3(1.0)))) {
|
||||
float coat_neta = 1.0 / coat_ior;
|
||||
float NT = fast_sqrt(1.0 - coat_neta * coat_neta * (1 - NV * NV));
|
||||
/* Tint lower layers. */
|
||||
coat_tint.rgb = mix(vec3(1.0), pow(coat_tint.rgb, vec3(1.0 / NT)), coat_weight);
|
||||
coat_tint.rgb = mix(vec3(1.0), pow(coat_tint.rgb, vec3(1.0 / NT)), saturate(coat_weight));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -139,13 +153,13 @@ void node_bsdf_principled(vec4 base_color,
|
|||
reflection_data.roughness = roughness;
|
||||
vec3 reflection_tint = specular_tint.rgb;
|
||||
if (metallic > 0.0) {
|
||||
vec3 F0 = base_color.rgb;
|
||||
vec3 F82 = reflection_tint;
|
||||
vec3 F0 = clamped_base_color.rgb;
|
||||
vec3 F82 = min(reflection_tint, vec3(1.0));
|
||||
vec3 metallic_brdf;
|
||||
brdf_f82_tint_lut(F0, F82, NV, roughness, do_multiscatter != 0.0, metallic_brdf);
|
||||
reflection_data.color = weight * metallic * metallic_brdf;
|
||||
/* Attenuate lower layers */
|
||||
weight *= (1.0 - metallic);
|
||||
weight *= max((1.0 - metallic), 0.0);
|
||||
}
|
||||
else {
|
||||
reflection_data.color = vec3(0.0);
|
||||
|
@ -162,7 +176,7 @@ void node_bsdf_principled(vec4 base_color,
|
|||
vec3 reflectance, transmittance;
|
||||
bsdf_lut(F0,
|
||||
F90,
|
||||
base_color.rgb,
|
||||
clamped_base_color.rgb,
|
||||
NV,
|
||||
roughness,
|
||||
ior,
|
||||
|
@ -175,7 +189,7 @@ void node_bsdf_principled(vec4 base_color,
|
|||
refraction_data.weight = weight * transmission_weight;
|
||||
refraction_data.color = transmittance * coat_tint.rgb;
|
||||
/* Attenuate lower layers */
|
||||
weight *= (1.0 - transmission_weight);
|
||||
weight *= max((1.0 - transmission_weight), 0.0);
|
||||
}
|
||||
else {
|
||||
refraction_data.weight = 0.0;
|
||||
|
@ -202,14 +216,14 @@ void node_bsdf_principled(vec4 base_color,
|
|||
|
||||
reflection_data.color += weight * reflectance;
|
||||
/* Attenuate lower layers */
|
||||
weight *= (1.0 - max_v3(reflectance));
|
||||
weight *= max((1.0 - max_v3(reflectance)), 0.0);
|
||||
}
|
||||
|
||||
/* Diffuse component */
|
||||
if (true) {
|
||||
diffuse_data.sss_radius = subsurface_radius * subsurface_scale;
|
||||
diffuse_data.sss_radius = max(subsurface_radius * subsurface_scale, vec3(0.0));
|
||||
diffuse_data.sss_id = uint(do_sss);
|
||||
diffuse_data.color += weight * base_color.rgb * coat_tint.rgb;
|
||||
diffuse_data.color += weight * diffuse_sss_base_color.rgb * coat_tint.rgb;
|
||||
}
|
||||
|
||||
/* Adjust the weight of picking the closure. */
|
||||
|
|
Nitpick, but for some weird corner cases, clamping the product instead of the individual factors might be nicer?