Diffuse Proxima + Specular Schlick-GGX | The Callisto Protocol Lighting 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.2.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 BRDF(
	in vec3 n,
	in vec3 l,
	in vec3 v,
	in vec3 diffuse_color,
	in float roughness,
	in float metallic,
	in float specular,
	in vec3 retroreflection_tint,
	in float retroreflection,
	in float retroreflection_falloff,
	in float retroreflection_tangent_falloff,
	in vec3 diffuse_fresnel_tint,
	in float diffuse_fresnel,
	in float diffuse_fresnel_falloff,
	in float diffuse_fresnel_tangent_falloff,
	in float specular_fresnel_falloff,
	in float smooth_terminator,
	in float smooth_terminator_length,
	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 alpha = roughness * roughness;
	float alpha2 = alpha * alpha;
	
	float HdotN = dot(h, n); // cos(theta_h).
	float HdotL = dot(h, l); // cos(theta_d).
	
	float VdotL = max(dot(-v, l), 0.0); // cos(theta_k).
	
	//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
	
	/* Diffuse Proxima */
	vec3 rho_f = diffuse_fresnel * diffuse_fresnel_tint;
	vec3 rho_r = retroreflection * retroreflection_tint;
	
	float alpha_f = _h(HdotL, _r(diffuse_fresnel_falloff), HdotN, _r(diffuse_fresnel_tangent_falloff));
	float alpha_r = _h(HdotN, _r(retroreflection_falloff), HdotL, _r(retroreflection_tangent_falloff));
	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 * smooth_terminator_length, NdotL), alpha_s * smooth_terminator);
	
	fd = c1 * INV_PI * (alpha * (-0.55 + 0.19 * (1.0 / NdotL)) * (1.0 - sqrt(VdotL)) + 1.0) * c2 * NdotL;
	
	/* Specular Trowbridge-Reitz-GGX */
	
	/* 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 / (NdotL + sqrt(alpha2 + (1.0 - alpha2) * (NdotL * NdotL)));
	float GV = 1.0 / (NdotV + sqrt(alpha2 + (1.0 - alpha2) * (NdotV * NdotV)));
	
	float G = GL * GV;
	
	/* Fresnel Function (Schlick’s Approximation) */
	vec3 f0 = mix(vec3(specular * 0.08), diffuse_color, vec3(metallic));
	float FH = pow(clamp(1.0 - HdotL, 0.0, 1.0), 5.0 * _r(specular_fresnel_falloff));
	
	vec3 F = f0 + _t(2.0 - _r(specular_fresnel_falloff)) * (1.0 - f0) * FH;
	
	fs = D * G * F * c2 * NdotL;
}

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 fd = vec3(0.0);
	vec3 fs = vec3(0.0);
	
	BRDF(
	n,
	l,
	v,
	ALBEDO,
	ROUGHNESS,
	METALLIC,
	_Specular,
	_RetroreflectionTint,
	_Retroreflection,
	_RetroreflectionFalloff,
	_RetroreflectionTangentFalloff,
	_DiffuseFresnelTint,
	_DiffuseFresnel,
	_DiffuseFresnelFalloff,
	_DiffuseFresnelTangentFalloff,
	_SpecularFresnelFalloff,
	_SmoothTerminator,
	_SmoothTerminatorLength,
	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;
}
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