shader_type spatial; render_mode unshaded, fog_disabled, depth_draw_never, depth_test_disabled, cull_disabled; // Screen-space ink outline: detects depth and normal discontinuities and // draws thin dark edges over the scene — the drawn-line pass that unifies // every model (kit, hero, character) into the cel look. Attach to a // fullscreen quad (POSITION override) so it runs for whatever camera renders. uniform sampler2D screen_tex : hint_screen_texture, filter_linear; uniform sampler2D depth_tex : hint_depth_texture, filter_nearest; uniform sampler2D normal_tex : hint_normal_roughness_texture, filter_nearest; uniform vec4 ink_color : source_color = vec4(0.07, 0.06, 0.09, 1.0); uniform float depth_threshold : hint_range(0.0, 4.0) = 1.1; uniform float normal_threshold : hint_range(0.0, 1.0) = 0.55; uniform float edge_strength : hint_range(0.0, 1.0) = 0.8; uniform float max_distance : hint_range(10.0, 500.0) = 220.0; float linear_depth(vec2 uv, mat4 inv_proj) { float d = texture(depth_tex, uv).r; vec4 ndc = vec4(uv * 2.0 - 1.0, d, 1.0); vec4 view = inv_proj * ndc; return -view.z / view.w; } void vertex() { POSITION = vec4(VERTEX.xy, 1.0, 1.0); } void fragment() { vec2 px = 1.0 / VIEWPORT_SIZE; vec2 uv = SCREEN_UV; float dc = linear_depth(uv, INV_PROJECTION_MATRIX); float dl = linear_depth(uv - vec2(px.x, 0.0), INV_PROJECTION_MATRIX); float dr = linear_depth(uv + vec2(px.x, 0.0), INV_PROJECTION_MATRIX); float du = linear_depth(uv - vec2(0.0, px.y), INV_PROJECTION_MATRIX); float dd = linear_depth(uv + vec2(0.0, px.y), INV_PROJECTION_MATRIX); // Depth edge: neighbor difference scaled by distance so far geometry // doesn't dissolve into noise. float ddiff = abs(dl - dr) + abs(du - dd); float depth_edge = step(depth_threshold * (0.5 + dc * 0.08), ddiff); // Normal edge: crease lines on same-depth corners. vec3 nc = texture(normal_tex, uv).xyz * 2.0 - 1.0; vec3 nl = texture(normal_tex, uv - vec2(px.x, 0.0)).xyz * 2.0 - 1.0; vec3 nu = texture(normal_tex, uv - vec2(0.0, px.y)).xyz * 2.0 - 1.0; float ndiff = length(nc - nl) + length(nc - nu); float normal_edge = step(normal_threshold, ndiff) * step(ddiff, depth_threshold); float fade = 1.0 - smoothstep(max_distance * 0.6, max_distance, dc); float edge = max(depth_edge, normal_edge * 0.6) * edge_strength * fade; vec3 scene = texture(screen_tex, uv).rgb; ALBEDO = mix(scene, ink_color.rgb, edge); }