Feat/visual audio overhaul #21
@@ -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);
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user