Diffuse Proxima + Specular Schlick-GGX | The Callisto Protocol Light Model

Lighting model developed by Jose Naranjo, Jon Diego, Jay Ryness, and Miguel Rodriguez for the callisto protocol shown in the GDC talk available here.

Shader code
// ElSuicio, 2026.
// GODOT v4.6.1.stable.
// x.com/ElSuicio
// github.com/ElSuicio
// Contact email [interdreamsoft@gmail.com]

shader_type spatial;

const float INV_PI = 0.31830988618379067154;

group_uniforms _ProximaGGX;
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;

group_uniforms _ProximaGGX._General;
uniform float _SmoothTerminator : hint_range(-1.0, 1.0, 1e-3) = 0.0;
uniform float _SmoothTerminatorLength : hint_range(0.0, 1.0, 1e-3) = 0.5;

group_uniforms _ProximaGGX._Diffuse;
uniform vec3 _DiffuseFresnelTint : source_color = vec3(1.0);
uniform float _DiffuseFresnel : hint_range(0.0, 256.0, 1e-3) = 1.0;
uniform float _DiffuseFresnelFalloff : hint_range(0.0, 1.0, 1e-3) = 0.75;
uniform float _DiffuseFresnelTangentFalloff : hint_range(0.0, 1.0, 1e-3) = 0.75;

uniform vec3 _RetroreflectionTint : source_color = vec3(1.0);
uniform float _Retroreflection : hint_range(0.0, 256.0, 1e-3) = 1.0;
uniform float _RetroreflectionFalloff : hint_range(0.0, 1.0, 1e-3) = 0.75;
uniform float _RetroreflectionTangentFalloff : hint_range(0.0, 1.0, 1e-3) = 0.75;

group_uniforms _ProximaGGX._Specular;
uniform float _SpecularFresnelFalloff : hint_range(0.0, 1.0, 1e-3) = 0.5;

float _h(
	in float cos_theta,
	in float n,
	in float cos_phi,
	in float m
)
{
	return clamp(pow(1.0 - cos_theta, 5.0 * n) * pow(cos_phi, 5.0 * m), 0.0, 1.0);
}

float _r(
	in float x
)
{
	return 2.0 * (1.0 - x);
}

float _t(
	in float x
)
{
	return min(max(x, 0.0), 1.0);
}

void fragment() {
	ALBEDO = _DiffuseColor;
	ROUGHNESS = _Roughness;
	METALLIC = _Metallic;
	SPECULAR = _Specular;
}

void light() {
	vec3 n = normalize(NORMAL);
	vec3 l = normalize(LIGHT);
	vec3 v = normalize(VIEW);
	
	vec3 h = normalize(v + l);
	
	float alpha = ROUGHNESS * ROUGHNESS;
	float alpha2 = alpha * alpha;
	
	float NdotL = dot(n, l); // cos(theta_l) == cos(theta_i).
	float NdotV = dot(n, v); // cos(theta_v) == cos(theta_r).
	
	float HdotN = dot(h, n); // cos(theta_h).
	float HdotL = dot(h, l); // cos(theta_d).
	
	float VdotL = dot(-v, l); // cos(theta_k).
	
	float cNdotL = max(NdotL, 0.0); // max(cos(theta_i), 0.0).
	float cNdotV = max(NdotV, 0.0); // max(cos(theta_r), 0.0).
	
	float cHdotN = max(HdotN, 0.0); // max(cos(theta_h), 0.0).
	float cHdotL = max(HdotL, 0.0); // max(cos(theta_d), 0.0).
	
	float cVdotL = max(VdotL, 0.0); // max(cos(theta_k), 0.0).
	
	//float theta_i = acos(NdotL);
	//float theta_r = acos(NdotV);
	
	float theta_h = acos(HdotN);
	float theta_d = acos(HdotL);
	
	// https://gdcvault.com/play/1029339/The-Character-Rendering-Art-of
	// https://advances.realtimerendering.com/s2023/SIGGRAPH2023-Advances-The-Rendering-of-The-Callisto-Protocol-JimenezPetersen.pdf
	
	/* Proxima-GGX */
	vec3 rho_f = _DiffuseFresnel * _DiffuseFresnelTint;
	vec3 rho_r = _Retroreflection * _RetroreflectionTint;
	
	float alpha_f = _h(cHdotL, _r(_DiffuseFresnelFalloff), cHdotN, _r(_DiffuseFresnelTangentFalloff));
	float alpha_r = _h(cHdotN, _r(_RetroreflectionFalloff), cHdotL, _r(_RetroreflectionTangentFalloff));
	float alpha_s = (1.0 - pow(1.0 - theta_d, 3.0)) * (1.0 - pow(1.0 - theta_h, 3.0));
	
	vec3 c1 = mix(vec3(1.0), rho_f, alpha_f) * mix(vec3(1.0), rho_r, alpha_r);
	float c2 = mix(1.0, smoothstep(0.0, alpha_s * _SmoothTerminatorLength, NdotL), alpha_s * _SmoothTerminator);
	
	/* Normal Distribution Function (GGX) */
	float t = 1.0 + (alpha2 - 1.0) * HdotN * HdotN;
	float D = alpha2 / (PI * t * t);
	
	/* Geometric Function (Smith-GGX) */
	float GL = 1.0 / (cNdotL + sqrt(alpha2 + (1.0 - alpha2) * (cNdotL * cNdotL)));
	float GV = 1.0 / (cNdotV + sqrt(alpha2 + (1.0 - alpha2) * (cNdotV * cNdotV)));
	
	float G = GL * GV;
	
	/* Fresnel Function (Schlick’s Approximation) */
	vec3 f0 = mix(vec3(_Specular * 0.08), ALBEDO, vec3(METALLIC));
	float FH = pow(clamp(1.0 - HdotL, 0.0, 1.0), 5.0 * _r(_SpecularFresnelFalloff));
	
	vec3 F = f0 + _t(2.0 - _r(_SpecularFresnelFalloff)) * (1.0 - f0) * FH;
	
	/* Lambert */
	//vec3 f_d = c1 * INV_PI * c2 * cNdotL;
	
	/* Proxima */
	vec3 f_d = c1 * INV_PI * (alpha * (-0.55 + 0.19 * (1.0 / NdotL)) * (1.0 - sqrt(cVdotL)) + 1.0) * c2 * cNdotL;
	
	/* Trowbridge-Reitz-GGX */
	vec3 f_s = D * G * F * c2 * cNdotL;
	
	// To compare with the Slice-Image.
	//vec3 radiance = (LIGHT_COLOR / PI) * ATTENUATION;
	
	// In PI light units.
	vec3 radiance = LIGHT_COLOR * ATTENUATION;
	
	DIFFUSE_LIGHT += radiance * f_d;
	SPECULAR_LIGHT += radiance * f_s;
}

Live Preview
Tags
3d, diffuse, light, lighting, Specular
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from ElSuicio

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments