92 lines
4.3 KiB
Text
92 lines
4.3 KiB
Text
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;
|
||
}
|