shader_type spatial; // Wasser-Oberfläche pro Map (Karte t_28fdef03) — SPIEGELGLEICHE Datei: // scopedvr-client → res://shaders/wasser.gdshader // scopedvr-map-editor → res://src/viewport/wasser.gdshader (byteidentischer // Spiegel, sync via tools/sync_wasser.sh — wie terrain_splat/terrain_arrays). // // Quest-3-Perf-Vertrag (bewusste Absagen der Spec): KEINE echte Spiegelung, // KEINE Brechung, KEIN SSR. Spiegel-Impression = Skybox-Farbreflex (uniform) // + Fresnel-Glanz. Wellen NUR im Fragment-Shader (analytische 2-Layer-Sinus- // Normalen — keine Wellen-Texturen, keine Geometrie-Bewegung). Ufer-Schaum + // Flachwasser-Transparenz aus der Tiefe-Textur (ufer_tex, R8 = Tiefe/TIEFE_MAX, // siehe SvmapWasser.ufer_bild). Ferne Fragmente vereinfachen sich selbst // (Wasserdistanz-Regel): nur Grundfarbe, keine Wellen/Schaum-Mathematik. // // Eine Fläche, ein Draw-Call — Overdraw ist die einzige echte Kostenquelle. render_mode blend_mix, depth_draw_never, cull_back, unshaded, shadows_disabled; uniform vec4 farbe_flach : source_color = vec4(0.10, 0.30, 0.34, 1.0); uniform vec4 farbe_tief : source_color = vec4(0.02, 0.10, 0.15, 1.0); uniform vec3 reflex : source_color = vec3(0.72, 0.83, 0.92); // Skybox-Farbreflex (Wetter) uniform float glanz : hint_range(0.0, 2.0) = 1.0; // Glint-Stärke (Wetter) uniform sampler2D ufer_tex : filter_linear; // R = Tiefe 0..1 (0..TIEFE_MAX) uniform float tiefe_max = 4.0; // SvmapWasser.TIEFE_MAX (Spiegel-Konstante) uniform float schaum_band = 0.35; // SvmapWasser.SCHAUM_BAND (Meter) uniform float wellen_scale = 0.55; // Wellenfrequenz (1/m) uniform float wellen_staerke = 0.16; // Normal-Neigung (flach = Spiegel-Optik) varying vec3 welt_pos; // Welt-Position des Fragments (Fresnel + Distanz-Regel) void vertex() { welt_pos = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz; } // Analytische Wellen-Normale aus 2 Scroll-Ebenen (kleine Skala, verschiedene // Richtungen/Geschwindigkeiten — läuft nie synchron): h = Σ A·sin(dot(p,d)·f + t·s), // n ∝ (−∂h/∂x, 1, −∂h/∂z) mit ∂h/∂p = Σ A·f·d·cos(…). vec3 wellen_normal(vec2 p, float t) { vec2 d1 = vec2(0.94, 0.34); vec2 d2 = vec2(-0.42, 0.91); float f1 = wellen_scale * 2.1; float f2 = wellen_scale * 3.7; float s1 = 1.25; float s2 = 1.75; float a1 = wellen_staerke; float a2 = wellen_staerke * 0.6; vec2 g1 = d1 * (f1 * a1 * cos(dot(p, d1) * f1 + t * s1)); vec2 g2 = d2 * (f2 * a2 * cos(dot(p, d2) * f2 + t * s2)); vec2 g = g1 + g2; return normalize(vec3(-g.x, 1.0, -g.y)); } void fragment() { // Tiefe am Fragment (ufer_tex, UV deckt die Map 1:1 ab — SvmapWasser.ufer_bild). float tiefe01 = texture(ufer_tex, UV).r; float tiefe = tiefe01 * tiefe_max; // Wasserdistanz-Regel: ferne Fragmente = vereinfachter Modus (Grundfarbe, // flache Normale, kein Schaum). Weiches Fenster 45→90 m, pro Fragment. float fern = smoothstep(45.0, 90.0, length(welt_pos - CAMERA_POSITION_WORLD)); float detail = 1.0 - fern; vec3 n = wellen_normal(welt_pos.xz, TIME); n = normalize(mix(vec3(0.0, 1.0, 0.0), n, detail)); vec3 v = normalize(CAMERA_POSITION_WORLD - welt_pos); float fresnel = pow(1.0 - clamp(dot(n, v), 0.0, 1.0), 5.0); // Grundfarbe: seicht heller/durchsichtiger, tief dunkler. vec3 grund = mix(farbe_flach.rgb, farbe_tief.rgb, clamp(tiefe01 * 1.4, 0.0, 1.0)); // Spiegel-Impression: Skybox-Farbreflex via Fresnel + Sonnen-Glint. vec3 reflex_f = reflex * mix(0.25, 1.0, fresnel); vec3 l = normalize(vec3(-0.45, 0.75, 0.5)); // SvmapTerrain.RELIEF_SUN (Spiegel) float glint = pow(max(dot(reflect(-v, n), l), 0.0), 48.0) * glanz; // Ufer-Schaum: schmales Band an der Wasserlinie, Kante leicht wellig // (Wellen-Phase pulsiert das Band ±20 %). float schaum_grenze = schaum_band * (0.9 + 0.2 * sin(welt_pos.x * 1.7 + welt_pos.z * 1.3 + TIME * 1.2)); float schaum = (1.0 - smoothstep(0.0, max(schaum_grenze, 0.02), tiefe)) * detail; vec3 farbe = grund + reflex_f + vec3(glint) + vec3(schaum * 0.85); // Transparenz: Ufer fast klar, tiefes Wasser dicht. Schaum deckt voll. float alpha = mix(0.38, 0.88, clamp(tiefe01 * 1.6, 0.0, 1.0)); alpha = max(alpha, schaum * 0.9); // spatial-Vertrag: Farbe über ALBEDO, Transparenz über ALPHA (COLOR gibt // es nur im canvas_item-Shader — schreibend dort = „Constants cannot be // modified“). ALBEDO = farbe; ALPHA = alpha; }