#version 330 in vec2 fragTexCoord; in vec2 fragWorldPos; out vec4 finalColor; uniform sampler2D texture0; uniform float time; uniform vec4 foamColor = vec4(0.96, 0.99, 1.0, 1.0); uniform float foamAnimSpeed = 1.6; uniform float foamNoiseScale = 0.55; float hash(vec2 p) { p = fract(p * vec2(123.34, 456.21)); p += dot(p, p + 45.32); return fract(p.x * p.y); } float valueNoise(vec2 p) { vec2 i = floor(p); vec2 f = fract(p); float a = hash(i); float b = hash(i + vec2(1.0, 0.0)); float c = hash(i + vec2(0.0, 1.0)); float d = hash(i + vec2(1.0, 1.0)); vec2 u = f * f * (3.0 - 2.0 * f); return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; } void main() { vec4 tex = texture(texture0, fragTexCoord); if (tex.a >= 0.999) { finalColor = tex; return; } if (tex.a <= 0.001) { discard; } vec2 pixelized = floor(fragWorldPos); float n1 = valueNoise(pixelized * foamNoiseScale + vec2(time * foamAnimSpeed, 0.0)); float n2 = valueNoise(pixelized * foamNoiseScale * 2.1 - vec2(0.0, time * foamAnimSpeed * 0.7)); float churn = n1 * 0.6 + n2 * 0.4; float mask = tex.a * smoothstep(0.2, 0.7, churn + tex.a * 0.6); finalColor = vec4(foamColor.rgb, foamColor.a * mask); }