Files
Papay-Shooter/assets/shaders/toon.gdshader
T
Nicholas ButzkeandClaude Fable 5 b0f26e6dde feat: matte anime materials — kill the glass/mirror look, keep the vibrancy
Everything read as wet plastic or glowing porcelain for two stacking
reasons, both fixed centrally:

- toonify() shipped every character/prop/weapon with a hot rim (0.45 over
  65% of the silhouette) and broad stepped specular (0.25 @ shininess 24) —
  patent-leather highlights on any dark surface. Now a thin rim (0.15/0.4)
  and a small tight glint (0.06 @ 56); shader defaults match.
- The LINEAR tonemapper clipped lit white surfaces (sun + ambient push
  facades past 1.0) and the glow pass bloomed the clip, so buildings glowed
  like porcelain. Switched to FILMIC with white=2.4: near-linear through
  the cel bands (they stay crisp), soft shoulder above 1.0. Glow threshold
  1.1 -> 1.45 so only genuine emissives (neon, tracers, plasma) bloom.
- Saturation 1.22 -> 1.3 compensates filmic mids — vibrancy unchanged.
- Neon Alley: ambient 1.7 -> 1.35 / sun 2.2 -> 2.5 — the shoulder no longer
  hides the ambient lavender cast, so let the sun shape the facades.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-19 23:53:05 -04:00

83 lines
3.1 KiB
Plaintext

shader_type spatial;
// Cel/toon surface shader used across the game.
// - Banded (stepped) diffuse with a tinted shadow color instead of black
// - Soft rim light for silhouette pop
// - Optional world-space triplanar albedo (level geometry: no UVs needed,
// grid stays world-scaled) — otherwise standard UV sampling (characters)
uniform vec4 albedo_color : source_color = vec4(1.0);
uniform sampler2D albedo_texture : source_color, filter_linear_mipmap, repeat_enable;
uniform bool has_texture = true;
uniform bool use_triplanar = false;
uniform float triplanar_tile = 2.0; // world units per texture tile
uniform float band_edge : hint_range(-1.0, 1.0) = 0.05; // NdotL where light band starts
uniform float band_softness : hint_range(0.001, 0.5) = 0.04;
uniform float mid_band_edge : hint_range(-1.0, 1.0) = 0.55; // second, brighter band
uniform vec4 shadow_color : source_color = vec4(0.62, 0.65, 0.78, 1.0); // cool shadow tint
// Matte-anime defaults: a thin rim and a tiny tight glint. Anything hotter
// reads as wet plastic / patent leather on dark surfaces.
uniform float rim_strength : hint_range(0.0, 2.0) = 0.15;
uniform float rim_width : hint_range(0.0, 1.0) = 0.4;
uniform float specular_strength : hint_range(0.0, 1.0) = 0.06;
uniform float specular_shininess : hint_range(1.0, 128.0) = 56.0;
varying vec3 world_pos;
varying vec3 world_normal;
void vertex() {
world_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
world_normal = normalize((MODEL_MATRIX * vec4(NORMAL, 0.0)).xyz);
}
vec3 sample_triplanar(vec3 p, vec3 n) {
vec3 w = abs(n);
w = pow(w, vec3(4.0));
w /= (w.x + w.y + w.z);
vec2 uv_x = p.zy / triplanar_tile;
vec2 uv_y = p.xz / triplanar_tile;
vec2 uv_z = p.xy / triplanar_tile;
vec3 cx = texture(albedo_texture, uv_x).rgb;
vec3 cy = texture(albedo_texture, uv_y).rgb;
vec3 cz = texture(albedo_texture, uv_z).rgb;
return cx * w.x + cy * w.y + cz * w.z;
}
void fragment() {
vec3 base = albedo_color.rgb;
if (has_texture) {
if (use_triplanar) {
base *= sample_triplanar(world_pos, world_normal);
} else {
base *= texture(albedo_texture, UV).rgb;
}
}
ALBEDO = base;
ROUGHNESS = 1.0;
SPECULAR = 0.0;
// Rim: brighten grazing angles for that inked-silhouette pop.
float rim = 1.0 - clamp(dot(normalize(VIEW), NORMAL), 0.0, 1.0);
rim = smoothstep(1.0 - rim_width, 1.0, rim);
EMISSION = base * rim * rim_strength;
}
void light() {
float ndotl = dot(NORMAL, LIGHT);
// Shadowing folds into the band test so shadow edges band too.
float lit = ndotl * ATTENUATION;
float band = smoothstep(band_edge - band_softness, band_edge + band_softness, lit);
float mid = smoothstep(mid_band_edge - band_softness, mid_band_edge + band_softness, lit);
// 3 tones: shadow tint -> base band (0.82) -> full light.
float tone = mix(0.82, 1.0, mid);
vec3 shade = mix(shadow_color.rgb, vec3(tone), band);
DIFFUSE_LIGHT += ALBEDO * LIGHT_COLOR / PI * shade;
// Stepped specular dot for glossy toon highlights.
vec3 h = normalize(VIEW + LIGHT);
float spec = pow(clamp(dot(NORMAL, h), 0.0, 1.0), specular_shininess);
spec = smoothstep(0.5 - band_softness, 0.5 + band_softness, spec);
SPECULAR_LIGHT += LIGHT_COLOR * spec * specular_strength * band;
}