Disney PBS Lighting Model
Disney PBS Lighting Model based on Brent Burley’s paper here.
You can get nodes for Godot visual shading system from other lighting models here.
Shader code
// ElSuicio, 2026.
// GODOT v4.6.2.stable.
// x.com/ElSuicio
// github.com/ElSuicio
// Contact email [interdreamsoft@gmail.com]
shader_type spatial;
const float INV_PI = 0.31830988618379067154;
group_uniforms _Disney;
uniform vec3 _DiffuseColor : source_color = vec3(1.0);
uniform float _Roughness : hint_range(0.0, 1.0, 1e-3) = 1.0;
uniform float _Metallic : hint_range(0.0, 1.0, 1e-3) = 0.0;
uniform float _Specular : hint_range(0.0, 1.0, 1e-3) = 0.5;
uniform float _SpecularTint : hint_range(0.0, 1.0, 1e-3) = 0.0;
uniform float _Subsurface : hint_range(0.0, 1.0, 1e-3) = 0.0;
uniform float _Anisotropic : hint_range(0.0, 1.0, 1e-3) = 0.0;
uniform float _Sheen : hint_range(0.0, 1.0, 1e-3) = 0.0;
uniform float _SheenTint : hint_range(0.0, 1.0, 1e-3) = 0.5;
uniform float _Clearcoat : hint_range(0.0, 1.0, 1e-3) = 0.0;
uniform float _ClearcoatGloss : hint_range(0.0, 1.0, 1e-3) = 1.0;
varying vec3 _binormal_vs;
varying vec3 _tangent_vs;
// https://www.w3.org/WAI/GL/wiki/Relative_luminance
float relative_luminance(in vec3 color)
{
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
}
float schlick_fresnel(in float u)
{
float m = clamp(1.0 - u, 0.0, 1.0);
return m * m * m * m * m; // pow(m,5)
}
void BRDF(
in vec3 n,
in vec3 l,
in vec3 v,
in vec3 x,
in vec3 y,
in vec3 rho,
in float roughness,
in float metallic,
in float specular,
in float specular_tint,
in float subsurface,
in float anisotropic,
in float sheen,
in float sheen_tint,
in float clearcoat,
in float clearcoat_gloss,
inout vec3 fd,
inout vec3 fs,
)
{
float NdotL = dot(n, l); // cos(theta_l) == cos(theta_i).
if(NdotL < 0.0)
{
return;
}
float NdotV = min(max(dot(n, v), 1e-3), 1.0); // cos(theta_v) == cos(theta_r).
vec3 h = normalize(v + l);
float NdotH = dot(n, h); // cos(theta_h).
float HdotL = dot(h, l); // cos(theta_d).
float LdotX = dot(l, x);
float VdotX = dot(v, x);
float HdotX = dot(h, x);
float LdotY = dot(l, y);
float VdotY = dot(v, y);
float HdotY = dot(h, y);
float alpha = roughness * roughness;
//float alpha2 = alpha * alpha;
float luminance = relative_luminance(rho);
vec3 chroma = luminance > 0.0 ? rho / luminance : vec3(1.0);
// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
/* Burley */
float FD_l = schlick_fresnel(NdotL), FD_v = schlick_fresnel(NdotV);
float retroreflection = roughness * HdotL * HdotL;
float FD90 = 0.5 + 2.0 * retroreflection;
float burley = mix(1.0, FD90, FD_l) * mix(1.0, FD90, FD_v);
/* Subsurface */
float ss = mix(1.0, retroreflection, FD_l) * mix(1.0, retroreflection, FD_v);
ss = 1.25 * (ss * (1.0 / (NdotL + NdotV) - 0.5) + 0.5);
/* Anisotropic-GGX */
float aspect = sqrt(1.0 - anisotropic * 0.9);
float ax = max(1e-3, alpha / aspect);
float ay = max(1e-3, alpha * aspect);
float inv_ax = 1.0 / ax;
float inv_ay = 1.0 / ay;
/* Normal Distribution Function (GGX-Anisotropic) */
float hx = HdotX * inv_ax;
float hy = HdotY * inv_ay;
float t = hx * hx + hy * hy + NdotH * NdotH;
float D = 1.0 / (PI * ax * ay * t * t);
/* Geometric Function (Smith-GGX-Anisotropic) */
float lx = LdotX * ax;
float ly = LdotY * ay;
float vx = VdotX * ax;
float vy = VdotY * ay;
float GL = 1.0 / (NdotL + sqrt(lx * lx + ly * ly + NdotL * NdotL));
float GV = 1.0 / (NdotV + sqrt(vx * vx + vy * vy + NdotV * NdotV));
float G = GL * GV;
/* Fresnel Function */
vec3 f0 = mix(specular * 8e-2 * mix(vec3(1.0), chroma, specular_tint), rho, metallic);
float FH = schlick_fresnel(HdotL);
vec3 F = mix(f0, vec3(1.0), FH);
/* Sheen */
vec3 sh = FH * sheen * mix(vec3(1.0), chroma, sheen_tint);
/* Clearcoat */
/* Normal Distribution Function */
float clearcoat_alpha = mix(1e-1, 1e-3, clearcoat_gloss);
float clearcoat_alpha2 = clearcoat_alpha * clearcoat_alpha;
float Dr = (clearcoat_alpha2 - 1.0) / (PI * log(clearcoat_alpha2) * (1.0 + (clearcoat_alpha2 - 1.0) * NdotH * NdotH));
/* Geometric Function */
float GrL = 1.0 / (NdotL + sqrt(625e-4 + 9375e-4 * NdotL * NdotL));
float GrV = 1.0 / (NdotV + sqrt(625e-4 + 9375e-4 * NdotV * NdotV));
float Gr = GrL * GrV;
/* Fresnel Function */
float Fr = mix(4e-2, 1.0, FH);
fd = (INV_PI * mix(burley, ss, subsurface) + sh) * NdotL;
fs = (D * G * F + 0.25 * clearcoat * Dr * Gr * Fr) * NdotL;
}
void fragment() {
_tangent_vs = TANGENT;
_binormal_vs = BINORMAL;
ALBEDO = _DiffuseColor;
ROUGHNESS = _Roughness;
METALLIC = _Metallic;
SPECULAR = _Specular;
}
void light() {
vec3 n = normalize(NORMAL);
vec3 l = normalize(LIGHT);
vec3 v = normalize(VIEW);
vec3 x = normalize(_tangent_vs);
vec3 y = normalize(_binormal_vs);
vec3 fd = vec3(0.0);
vec3 fs = vec3(0.0);
BRDF(n, l, v, x, y, ALBEDO, ROUGHNESS, METALLIC, _Specular, _SpecularTint, _Subsurface, _Anisotropic, _Sheen, _SheenTint, _Clearcoat, _ClearcoatGloss, fd, fs);
// To compare with the Slice-Image.
//vec3 radiance = (LIGHT_COLOR / PI) * ATTENUATION;
// In PI light units.
vec3 radiance = LIGHT_COLOR * ATTENUATION;
DIFFUSE_LIGHT += radiance * fd;
SPECULAR_LIGHT += radiance * fs;
}




Nice one!