#ifdef HAS_NORMALMAP uniform sampler2D m_NormalMap; #endif #ifdef HAS_FOAMMAP uniform sampler2D m_FoamMap; #endif uniform vec4 m_Tint; uniform float m_FoamAmount; in vec2 vUV; in vec2 vTex1; in vec2 vTex2; out vec4 outFragColor; void main() { // ── Randabstand: 0 = Mitte, 1 = Rand ───────────────────────────────────── float edgeDist = abs(vUV.x * 2.0 - 1.0); // ── Tiefengradient ──────────────────────────────────────────────────────── // Ränder heller/transparenter (flaches Ufer), Mitte dunkler (tief) vec3 baseColor = m_Tint.rgb * (1.0 - edgeDist * 0.45); float baseAlpha = m_Tint.a * smoothstep(0.0, 0.18, 1.0 - edgeDist); // ── Dual-Layer Normal-Map: Specular + Wellenhelligkeit ──────────────────── float specular = 0.0; float ripple = 0.0; #ifdef HAS_NORMALMAP vec3 n1 = texture(m_NormalMap, vTex1).rgb * 2.0 - 1.0; vec3 n2 = texture(m_NormalMap, vTex2).rgb * 2.0 - 1.0; vec3 n = normalize(n1 + n2); // Feste Sonnenrichtung – gut für Bachlauf-Optik vec3 sunDir = normalize(vec3(0.5, 1.0, 0.3)); specular = pow(max(0.0, dot(n, sunDir)), 22.0); // Wellenhelligkeit variiert den Basiston leicht ripple = dot(n, vec3(0.0, 1.0, 0.0)) * 0.10; #endif // ── Schaum ──────────────────────────────────────────────────────────────── // Uferschaum wird stärker je näher am Rand; Wasserfälle extra float foamEdge = smoothstep(0.55, 1.0, edgeDist); float foamMask = clamp(foamEdge + m_FoamAmount * 0.75, 0.0, 1.0); #ifdef HAS_FOAMMAP float foamSample = texture(m_FoamMap, vTex1 * 0.4).r; foamMask *= foamSample; #else // Prozeduraler Fallback wenn keine Schaumtextur geladen float procFoam = (sin(vTex1.x * 6.283) * 0.5 + 0.5) * (sin(vTex1.y * 3.141) * 0.5 + 0.5); foamMask *= procFoam; #endif // ── Zusammenführen ──────────────────────────────────────────────────────── vec3 waterColor = baseColor + ripple + vec3(0.35, 0.45, 0.55) * specular; vec3 finalColor = mix(waterColor, vec3(1.0), foamMask); float finalAlpha = max(baseAlpha, foamMask * 0.9); outFragColor = vec4(clamp(finalColor, 0.0, 1.0), finalAlpha); }