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

#define hash(x) fract(sin(x) * 43758.5453123)

vec3 vibrantPalette(float t) { 
    return 0.5 + 0.5 * sin(6.28318 * (t + vec3(0.1, 0.9, 0.5))); 
}

float refinedNoise(float x, float scale) {
    const float spread = 0.1;
    float i = floor(x);
    float f = x - i;
    float s = smoothstep(0.5 - spread, 0.5 + spread, f);
    return (mix(floor(hash(i) * scale), floor(hash(i + 1.0) * scale), s) / (scale - 1.0)) - 0.3;
}

vec3 dynamicShift(vec3 pos) {
    return vec3(
        refinedNoise(pos.y * 0.05, 8.0) * 4.0,
        refinedNoise(pos.z * 0.06, 7.0) * 5.0,
        refinedNoise(pos.x * 0.04, 6.0) * 3.0
    );
}

float circleDistance(vec2 p, float radius) {
    return length(p) - radius;
}

vec3 rotateAround(vec3 vec, vec3 axis, float theta) {
    return mix(dot(axis, vec) * axis, vec, cos(theta)) + cross(axis, vec) * sin(theta) * 0.6;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    vec3 color = vec3(0.0);
    vec3 start = vec3(0.0, 0.0, -10.0), end = vec3(0.0);
    start.z += iTime * 3.5;
    end.z += iTime * 3.5;
    start += dynamicShift(start);
    end += dynamicShift(end);
    vec3 direction = normalize(end - start);
    vec3 lateral = vec3(direction.z, 0.0, -direction.x);
    float lightAccumulation = 0.0, distanceTraveled = 0.0;
    vec3 rayDir = mat3(lateral, cross(direction, lateral), direction) *
                  rotateAround(normalize(vec3(uv, 1.0)), vec3(0.0, 0.0, 1.0),
                  refinedNoise(iTime + hash(uv.x * uv.y * iTime) * 0.2, 5.0));
    
    for (int i = 0; i < 70; i++) {
        float idx = float(i) + 1.0;
        vec3 currentPos = start + rayDir * distanceTraveled;
        currentPos -= dynamicShift(currentPos);
        float illuminationValue = 0.0;
        vec3 tmp = currentPos;
        float scaleVal = 1.0;
        for (int j = 0; j < 4; j++) {
            float jIdx = float(j) + 1.0;
            illuminationValue += clamp(abs(dot(sin(tmp * 1.5), cos(tmp.yzx * 1.5)) * 0.7 - 0.1) / scaleVal, 0.0, 1.0);
            tmp = rotateAround(tmp, normalize(vec3(0.5, 0.5, 0.5)), 0.5 + jIdx * 0.3);
            tmp += tmp.yzx + jIdx * 4.0;
            scaleVal *= 1.4;
            tmp *= 1.1;
        }
        float depthMeasure = abs(circleDistance(currentPos.xy, 8.0)) - 1.5 - illuminationValue;
        currentPos = rotateAround(currentPos, vec3(0.0, 0.0, 1.0), dynamicShift(currentPos).x * 0.25 + currentPos.z * 0.7);
        float borderThickness = length(abs(currentPos.xy) - 0.5) - 0.2;
        depthMeasure = min(borderThickness, depthMeasure);
        float adjustAmount = max(0.001, borderThickness == depthMeasure ? abs(depthMeasure) : depthMeasure);
        lightAccumulation = adjustAmount;
        distanceTraveled += adjustAmount;
        color += (borderThickness == depthMeasure
                ? vibrantPalette(currentPos.z * 0.5) * (20.0 * exp(-3.0 * fract(currentPos.z * 0.1 + iTime)))
                : vec3(0.02)) * 0.1 / exp(idx * idx * lightAccumulation);
    }
    color = mix(color, vec3(0.5, 0.7, 0.6), 1.0 - exp(-0.03 * distanceTraveled));
    fragColor = vec4(color, 1.0);
}

void main() {
    vec2 fragCoord = vUV * iResolution.xy;
    mainImage(FragColor, fragCoord);
    FragColor.a *= uAlpha;
}
