#version 330 core
out vec4 FragColor;
in vec2 vUV;
uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;

const float PI = 3.14159265;
const float TwoPI = 2.0 * PI;

vec3 tri(vec3 p)
{
    return 1.0 - abs(p - round(p)) * 4.0;
}

vec3 fm(vec3 p)
{
    return tri(p + 0.7 * tri(1.9 * p + 0.8));
}

vec3 tribulence(vec3 p, float T)
{
    float angle = 2.399;
    float c = cos(angle);
    float s = sin(angle);
    mat2 R = mat2(c, s, -s, c);

    for (float i = 1e-3; i < 5.0; i += i)
    {
        p += fm(p.zyx / i * 0.7 + T * 0.0125) * i * 0.0125;
        p.xy *= R;
        p = p.zyx;
    }

    return p;
}

vec3 cmap(float x, float T)
{
    return exp2(cos(T * PI + 2.0 * PI * x + vec3(1.0, 2.0, 3.0)));
}

float kernel(float x, float dt)
{
    return 0.004 / (x * x + dt);
}

float sRGBencode(float C_linear)
{
    return C_linear > 0.0031308 ? (1.055 * pow(C_linear, 1.0 / 2.4) - 0.055) : (12.92 * C_linear);
}

vec3 sRGBencode(vec3 C_linear)
{
    C_linear = clamp(C_linear, 0.0, 1.0);
    return vec3(sRGBencode(C_linear.x), sRGBencode(C_linear.y), sRGBencode(C_linear.z));
}

vec3 lissajous(float t)
{
    float a = 9.0;
    float b = 6.0;
    float m = max(a, b);
    a /= m;
    b /= m;

    float d = iTime * 0.2;
    return sin(TwoPI * t * vec3(a, b, a) + vec3(d, 0.0, d + PI * 0.5));
}

float seg(vec3 a, vec3 b, vec3 p)
{
    b -= a;
    p -= a;
    return length(clamp(dot(p, b) / dot(b, b), 0.0, 1.0) * b - p);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
    vec3 color = vec3(0.0);
    float focal = 2.0;
    vec3 rd = normalize(vec3(uv, -focal));
    vec3 ro = vec3(0.0, 0.0, 2.0);
    float T = iTime * 0.7;
    float t = 0.0;

    for (float i = 0.0; i < 30.0; i++)
    {
        vec3 p = rd * t + ro;
        vec3 curve = lissajous(T);

        p = tribulence(p, T);
        p = p.zyx;
        p = tribulence(p, T);
        p = p.zyx;
        p = tribulence(p, T);
        p *= 2.0;
        p -= curve;

        vec3 seg_a = lissajous(T - 0.15);
        vec3 seg_b = lissajous(T);
        vec3 seg_c = lissajous(T + 0.15);

        float sdf = min(seg(seg_a, seg_b, p), seg(seg_b, seg_c, p)) - 5e-3;
        float dt = abs(sdf) * 0.4 + 1e-3;
        float K = kernel(sdf, dt);

        color += cmap(K + sdf * 0.25, T) * (K + sqrt(K) * 0.3) * exp2(-1.5 * dot(seg_c - p, seg_c - p)) * 2.0;
        T -= sdf * 0.01 + K * 0.1 - 0.005;

        t += dt;
        if (t > 1e2) break;
    }

    color = 1.0 - exp(-1.3 * pow(color, vec3(1.5)));
    color = sRGBencode(color);
    fragColor = vec4(color, 1.0);
}

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