#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

varying vec2 v_uv;

uniform sampler2D tDiffuse;
uniform float u_time;
uniform float u_frame_rate;
uniform float u_opacity;
uniform sampler2D u_flowmap;
uniform float u_min_noise;

vec4 toSRGB (vec4 color) {
    return vec4( pow(color.rgb, vec3(1.0 / 2.2)), color.a );
}

float random(vec2 co) {
    vec2 p = fract(co * vec2(443.897, 441.423));
    p += dot(p, p.yx + 19.19);
    return fract(p.x * p.y);
}

void main() {
    vec2 st = v_uv.xy;

    // flowmap - RG channels contain 2D flow vectors
    vec3 flow = texture2D(u_flowmap, st).rgb;

    // Convert flow from [0,1] range to [-1,1] range
    vec2 flowVector = (flow.rg * 2.0 - 1.0);

    // Calculate flow magnitude (how much disturbance/scatter)
    float flowMagnitude = length(flowVector * 0.01);

    // Invert so high flow = min noise, low flow = 1 (full noise)
    // Adjust the power (2.0) to control falloff sharpness
    float maskValue = 1.0 - smoothstep(0.0, 1.0, flowMagnitude * 1.0);
    // float maskValue = 1.0 - pow(flowMagnitude, 2.0);
    float noiseMask = mix(u_min_noise, 1.0, maskValue);

    // Get input color from three.js
    vec4 original = texture2D(tDiffuse, st);
    original = toSRGB(original);

    // Random value for each pixel at specified frame rate
    float rnd = random(st + floor(u_time * u_frame_rate) / 10000.);

    // Apply noise mask - noise only appears where flow is weak
    vec4 noise = vec4(vec3(step(0.5, rnd)), u_opacity * noiseMask);

    vec4 color = mix(original, noise, noise.a);

    gl_FragColor = color;
}
