#version 330 core

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform vec2 iMouse;

out vec4 FragColor;

/*

    Neon Raindrops by chronos
    --------------------------
    
    Created by adding layers of domain repeated cells of inverted pyramids falling down.

*/
#define DEBUG 0

const float PI = 3.14159265;
float sRGBencode(float C_linear) { return C_linear > 0.0031308 ? (1.055 * pow(C_linear, 1./2.4) - 0.055) : (12.92 * C_linear); }
vec3 sRGBencode(vec3 C_linear) { C_linear = clamp(C_linear, 0., 1.); return vec3(sRGBencode(C_linear.x), sRGBencode(C_linear.y), sRGBencode(C_linear.z)); }

float hash(vec3 uv)
{
    uint x = floatBitsToUint(uv.x) | 1u; // 0 is a fixed point so we remove it. although this introduces duplicate 1
    uint y = floatBitsToUint(uv.y);
    uint z = floatBitsToUint(uv.z);
    
    y ^= y >> 13;
    y ^= y << 17;
    y ^= y >> 5;
    y *= 0x2545F491u;

    x ^= y;
    x ^= x >> 13;
    x ^= x << 17;
    x ^= x >> 5;
    x *= 0x4F6CDD1Du;
    
    z ^= x;
    z ^= z >> 13;
    z ^= z << 17;
    z ^= z >> 5;
    z *= 0x1D6C45F4u;
    
    uint w = (z>>9) | 0x3f000000u; // simplified version
    return 2. * uintBitsToFloat(w) - 1.;
}

vec3 drops(vec2 uv)
{
    vec3 color = vec3(0);
    float hash_cnt = 0.;
    float grid_size = 20.;
    vec2 g = cos(grid_size * (1.+uv) * PI);
    float grid = smoothstep(0.98, 0.99, max(g.x, g.y));
    grid += (.0001/(1.5+max(g.x, g.y)) + grid*(40./255.)*hash(vec3(uv, 0.)));

    vec2 mask_uv = abs(uv);
    float square_mask = smoothstep(1.01, 1.0, max(mask_uv.x, mask_uv.y));
    float disc_mask   = smoothstep(1., .66, length(uv));

    float circle = smoothstep(0.01, 0.005, abs(length(uv)-1.));

    for(int i = 0; i < 7; i++)
    {
        int anim_instance = i;
        float time_offset = hash(vec3(anim_instance, 1., hash_cnt++));
        float speed_offset = hash(vec3(anim_instance, 1., hash_cnt++));

        float speed = 0.25 * mix(0.8, 1.2, speed_offset);

        float t = speed * iTime + time_offset;
        float drop_cycle = fract(t);
        float drop_instance = floor(t);

        vec3 L = normalize(vec3(1));
        vec3 U = normalize(vec3(2,-1,-1)/3.);
        vec3 V = cross(L, U);
        float hue = 2. * PI * hash(vec3(drop_instance, anim_instance, hash_cnt++));
        vec3 pulse_color = clamp(.5 * L + U * cos(hue) + V * sin(hue), 0., 1.);

        vec2 pos = vec2(
            -.5+hash(vec3(drop_instance, 1., hash_cnt++)),
            -.5+hash(vec3(drop_instance, 1., hash_cnt++))
        );
        vec2 p = abs(uv-pos);
        float pulse_tail_len = 0.5;
        float f = p.x + p.y - (pulse_tail_len + 2. * sqrt(2.)) * drop_cycle;
        float pulse = max(1.-abs(f)/pulse_tail_len, 0.) * exp(-abs(f))/(0.001 + 100.*abs(f));
 
        pulse_color = mix(pulse_color, vec3(1), 0.1*smoothstep(0.9, .95, pulse));
        
        pulse = mix(pulse, pulse*smoothstep(0.01, 0.00, f), .8);
 
        color += pulse * pulse_color * grid;
    }
    
    color += 0.01 * (.075/(1.5+max(g.x, g.y)) + (40./255.)*hash(vec3(uv, 0.)));
    
    color *= mix(0.0, 1., disc_mask);
    color *= square_mask;
    
    #if DEBUG
    color += circle;
    #endif
    
    return color;
} 

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = (2. * fragCoord-iResolution.xy)/ iResolution.y;
    vec2 m = (2. * iMouse.xy-iResolution.xy)/ iResolution.y;
    vec3 color = vec3(0);
    if(length(iMouse.xy) < 10.) m = cos(vec2(iTime, iTime - PI/2.)) * 0.05 - vec2(0, 0.2);
    
    #if DEBUG
    color = drops(uv);
    color = sqrt(tanh(color * color));
    color = sRGBencode(color);
    fragColor = vec4(color, 1);
    return;
    #endif
    
    float focal = 1.;
    vec3 ro = vec3(0, 1., 1.5);
    vec3 rd = vec3(uv, -focal);
    
    float angle = m.y * PI;
    float c = cos(angle), s = sin(angle);
    mat2 R = mat2(c,s,-s, c);
    rd.yz = R * rd.yz;
    
    angle = m.x * PI;
    c = cos(angle), s = sin(angle);
    R = mat2(c,s,-s, c);
    rd.xz = R * rd.xz;
    
    float t = -ro.y/ rd.y;
    if(t > 0.)
    {
        vec3 ray_hit_pos = ro + t * rd;
        color = drops(.25 * ray_hit_pos.xz);
    }
    else
    {
        fragColor = vec4(0,0,0,1);
        return;
    }
    
    color = sqrt(tanh(color * color));
    color = sRGBencode(color);
    fragColor = vec4(color, 1);
}

void main()
{
    vec4 fc;
    mainImage(fc, gl_FragCoord.xy);
    fc.a *= uAlpha;
    FragColor = fc;
}
