Shader: Only clamp undefined or unsupported inputs of Principled BSDF #112895

Merged
Lukas Stockner merged 39 commits from Alaska/blender:clamp-tint into blender-v4.0-release 2023-10-31 03:14:14 +01:00
3 changed files with 93 additions and 58 deletions

View File

@ -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

Nitpick, but for some weird corner cases, clamping the product instead of the individual factors might be nicer?

Nitpick, but for some weird corner cases, clamping the product instead of the individual factors might be nicer?
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

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.

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`.

Part of the clamping comes from if TransmissionWeight > CLOSURE_WEIGHT_CUTOFF. However I have added back the explicit clamp for code clarity.

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

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));

View File

@ -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

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

I'm not sure where?

I'm not sure where?

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.

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);

View File

@ -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);

I think this should be saturate(base_color).

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. */