uniform sampler2D m_ExploreMap; uniform float m_Time; uniform vec2 m_OverlayOrigin; uniform vec2 m_OverlaySize; uniform float m_FadePx; in vec2 texCoord; out vec4 outFragColor; // ── Procedural Value Noise ──────────────────────────────────────────────────── float hash(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973)); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } float vnoise(vec2 p) { vec2 i = floor(p); vec2 f = fract(p); f = f * f * (3.0 - 2.0 * f); return mix( mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x), mix(hash(i + vec2(0.0,1.0)), hash(i + vec2(1.0, 1.0)), f.x), f.y); } // FBM: 3 Oktaven float fbm(vec2 p) { float v = 0.0; v += vnoise(p) * 0.500; v += vnoise(p * 2.1 + vec2(1.7, 9.2)) * 0.250; v += vnoise(p * 4.3 + vec2(8.3, 2.8)) * 0.125; return v / 0.875; } // ── Hauptprogramm ───────────────────────────────────────────────────────────── void main() { // Weichgezeichnete Erkundungsmaske (3×3-Kernel, 8-Texel-Radius = ~32 m) float ts = 8.0 / 512.0; float density = texture(m_ExploreMap, texCoord + vec2(-ts,-ts)).a + texture(m_ExploreMap, texCoord + vec2(0.0,-ts)).a + texture(m_ExploreMap, texCoord + vec2( ts,-ts)).a + texture(m_ExploreMap, texCoord + vec2(-ts, 0.0)).a + texture(m_ExploreMap, texCoord ).a + texture(m_ExploreMap, texCoord + vec2( ts, 0.0)).a + texture(m_ExploreMap, texCoord + vec2(-ts, ts)).a + texture(m_ExploreMap, texCoord + vec2(0.0, ts)).a + texture(m_ExploreMap, texCoord + vec2( ts, ts)).a; density /= 9.0; // Vollständig erkundet → diesen Fragment überspringen if (density <= 0.0) { discard; } // ── Animierter Nebel mit Domain-Warping ─────────────────────────────────── float t = m_Time * 0.18; vec2 uv = texCoord; // Erste Warp-Schicht: langsam driftende Wirbel vec2 q = vec2( fbm(uv * 2.5 + vec2(t * 0.40, t * 0.15)), fbm(uv * 2.5 + vec2(t * 0.15, t * 0.30) + vec2(5.2, 1.3)) ); // Zweite Warp-Schicht: gibt Tiefe und Komplexität vec2 r = vec2( fbm(uv * 2.0 + q * 0.9 + vec2(1.7, 9.2) + vec2(t * 0.10, 0.0)), fbm(uv * 2.0 + q * 0.9 + vec2(8.3, 2.8) + vec2(0.0, t * 0.08)) ); // Finaler Noise-Wert [0..1] float f = fbm(uv * 3.0 + r * 1.2 + vec2(t * 0.05, t * 0.04)); // ── Farbe + Alpha ───────────────────────────────────────────────────────── // Dunkles Blaugrau – etwas heller an den "Schwaden" vec3 fogColor = mix( vec3(0.04, 0.05, 0.09), // tiefer Schatten vec3(0.11, 0.13, 0.20), // heller Dunst f ); // Weich-Übergang am Overlay-Rand (Screen-Space, zoom-unabhängig) float distL = gl_FragCoord.x - m_OverlayOrigin.x; float distR = (m_OverlayOrigin.x + m_OverlaySize.x) - gl_FragCoord.x; float distB = gl_FragCoord.y - m_OverlayOrigin.y; float distT = (m_OverlayOrigin.y + m_OverlaySize.y) - gl_FragCoord.y; float edgeFade = clamp(min(min(distL, distR), min(distB, distT)) / m_FadePx, 0.0, 1.0); // Alpha: voll opak im unentdeckten Innern, weicher Rand durch Kernel + Edge-Fade float alpha = density * (0.88 + f * 0.12) * edgeFade; outFragColor = vec4(fogColor, alpha); }