Feat/visual audio overhaul #21

Merged
Dotts merged 31 commits from feat/visual-audio-overhaul into main 2026-07-21 14:09:18 -07:00
2 changed files with 71 additions and 0 deletions
Showing only changes of commit 5224a455a6 - Show all commits
+57
View File
@@ -0,0 +1,57 @@
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);
}
+14
View File
@@ -34,6 +34,20 @@ static func add_to(level: Node, sky_variant: String = "day") -> WorldEnvironment
fill.shadow_enabled = false fill.shadow_enabled = false
level.add_child(fill) level.add_child(fill)
# Screen-space ink edge pass: drawn outlines on every model for whatever
# camera renders (fullscreen POSITION-override quad, never culled).
if not level.has_node("InkEdgePost"):
var ink := MeshInstance3D.new()
ink.name = "InkEdgePost"
var quad := QuadMesh.new()
quad.size = Vector2(2, 2)
var ink_mat := ShaderMaterial.new()
ink_mat.shader = load("res://assets/shaders/ink_edge.gdshader")
quad.material = ink_mat
ink.mesh = quad
ink.extra_cull_margin = 16384.0
level.add_child(ink)
# Quiet ambient bed so the arena never sits in dead air. # Quiet ambient bed so the arena never sits in dead air.
if not level.has_node("AmbientBed") and ResourceLoader.exists("res://assets/sounds/wind.wav"): if not level.has_node("AmbientBed") and ResourceLoader.exists("res://assets/sounds/wind.wav"):
var amb := AudioStreamPlayer.new() var amb := AudioStreamPlayer.new()