Files
test2/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl
Lukas Stockner 9254532b8b Fix #129306: Cycles: Principled coat doesn't pass furnace test
This implements three improvements to the energy preservation and albedo
scaling logic, which help the Principled BSDF pass the white-furnace test
when using the coat layers at high roughness.

Specifically, at roughness 0.3, the albedo scaling brings it from 60% at
the edge to 95%, and with the energy preservation it's 99.8%.

Pull Request: https://projects.blender.org/blender/blender/pulls/134620
2025-02-26 15:47:21 +01:00

190 lines
7.8 KiB
Plaintext

/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "node_fresnel.h"
#include "stdcycles.h"
shader node_principled_bsdf(string distribution = "multi_ggx",
string subsurface_method = "random_walk",
color BaseColor = color(0.8, 0.8, 0.8),
float SubsurfaceWeight = 0.0,
float SubsurfaceScale = 0.1,
vector SubsurfaceRadius = vector(1.0, 1.0, 1.0),
float SubsurfaceIOR = 1.4,
float SubsurfaceAnisotropy = 0.0,
float Metallic = 0.0,
float DiffuseRoughness = 0.0,
float SpecularIORLevel = 0.5,
color SpecularTint = color(1.0),
float Roughness = 0.5,
float Anisotropic = 0.0,
float AnisotropicRotation = 0.0,
float SheenWeight = 0.0,
float SheenRoughness = 0.5,
color SheenTint = 0.5,
float CoatWeight = 0.0,
float CoatRoughness = 0.03,
float CoatIOR = 1.5,
color CoatTint = color(1.0, 1.0, 1.0),
float IOR = 1.45,
float TransmissionWeight = 0.0,
color EmissionColor = 1.0,
float EmissionStrength = 0.0,
float Alpha = 1.0,
float ThinFilmThickness = 0.0,
float ThinFilmIOR = 1.33,
normal Normal = N,
normal CoatNormal = N,
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 diffuse_roughness = clamp(DiffuseRoughness, 0.0, 1.0);
float r2 = clamp(Roughness, 0.0, 1.0);
r2 = r2 * r2;
float alpha_x = r2, alpha_y = r2;
/* Handle anisotropy. */
vector T = Tangent;
if (Anisotropic > 0.0) {
float aspect = sqrt(1.0 - clamp(Anisotropic, 0.0, 1.0) * 0.9);
alpha_x /= aspect;
alpha_y *= aspect;
if (AnisotropicRotation != 0.0)
T = rotate(T, AnisotropicRotation * M_2PI, point(0.0, 0.0, 0.0), Normal);
}
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);
eta = ior_from_F0(f0);
if (IOR < 1.0) {
eta = 1.0 / eta;
}
}
if (diffuse_roughness > CLOSURE_WEIGHT_CUTOFF) {
BSDF = oren_nayar_diffuse_bsdf(Normal, base_color, diffuse_roughness);
}
else {
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,
radius,
clamped_base_color,
"roughness",
r2,
"ior",
subsurface_ior,
"anisotropy",
SubsurfaceAnisotropy);
BSDF = mix(BSDF, clamped_base_color * SubsurfBSDF, subsurface_weight);
}
if (eta != 1.0 || ThinFilmThickness > 0.1) {
/* Apply specular tint */
color F0 = f0 * specular_tint;
color F90 = color(1.0);
BSDF = layer(generalized_schlick_bsdf(Normal,
T,
color(1.0),
color(0.0),
alpha_x,
alpha_y,
F0,
F90,
-eta,
distribution,
"thinfilm_thickness",
ThinFilmThickness,
"thinfilm_ior",
ThinFilmIOR),
BSDF);
}
}
closure color TransmissionBSDF = 0;
if (metallic < 1.0 && TransmissionWeight > CLOSURE_WEIGHT_CUTOFF) {
float eta = max(IOR, 1e-5);
float thinfilm_ior = backfacing() ? ThinFilmIOR / eta : ThinFilmIOR;
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(clamped_base_color),
r2,
r2,
F0,
F90,
-eta,
distribution,
"thinfilm_thickness",
ThinFilmThickness,
"thinfilm_ior",
thinfilm_ior),
BSDF = mix(BSDF, TransmissionBSDF, transmission);
}
closure color MetallicBSDF = 0;
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, metallic);
}
if (EmissionStrength != 0.0 && EmissionColor != color(0.0)) {
BSDF += EmissionStrength * EmissionColor * emission();
}
if (coat_weight > CLOSURE_WEIGHT_CUTOFF) {
float coat_ior = max(CoatIOR, 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), clamp(coat_weight, 0.0, 1.0));
}
float coat_r2 = clamp(CoatRoughness, 0.0, 1.0);
coat_r2 = coat_r2 * coat_r2;
closure color CoatBSDF = dielectric_bsdf(
CoatNormal, vector(0.0), color(1.0), color(0.0), coat_r2, coat_r2, coat_ior, "multi_ggx");
BSDF = layer(coat_weight * CoatBSDF, BSDF);
}
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(sheen_weight * max(SheenTint, color(0.0)) * SheenBSDF, BSDF);
}
BSDF = mix(transparent(), BSDF, clamp(Alpha, 0.0, 1.0));
}