#version 330 core
out vec4 FragColor;
in vec2 vUV;

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform float uDensity;
uniform float uBrightness;
uniform float uPassthrough;
uniform int uIterations;
uniform vec3 uColorA;
uniform vec3 uColorB;

mat2 rot(float a)
{
    return mat2(cos(a), -sin(a), sin(a), cos(a));
}

float volume(vec3 p)
{
    p.z += iTime * 2.0;
    p.xy *= rot(iTime * 0.3);
    p.xy *= rot(length(p.xy) * cos(p.z));
    p.x += cos(p.z + iTime * 0.3);
    p.y += sin(p.z + iTime * 0.2);
    p = fract(p) - 0.5;

    p.z += sin(p.x * 0.05) * 10.0;

    float sp = length(p.xy) - 0.1 * abs(cos(p.z));
    sp = abs(sp) - 0.001;

    return sp / max(uDensity, 0.001) + uPassthrough;
}

vec3 renderVolume(vec3 ro, vec3 rd)
{
    vec3 p = ro;
    vec3 col = vec3(0.0);

    for (int i = 0; i < 192; i++)
    {
        if (i >= uIterations)
            break;

        float vol = volume(p);
        vec3 acol = mix(uColorA, uColorB, cos(iTime + length(p.xy)) * 0.5 + 0.5);
        col += acol / max(abs(vol), 0.0001);
        p += abs(vol) * rd;
    }

    return tanh(col * max(uBrightness, 0.0));
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);
    vec3 ro = vec3(0.0, 0.0, -10.0);
    vec3 rd = normalize(vec3(uv, 1.0));
    vec3 col = renderVolume(ro, rd);

    fragColor = vec4(col, uAlpha);
}

void main()
{
    mainImage(FragColor, gl_FragCoord.xy);
}
