#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 palette(float t) { 
    return 0.5 + 0.5 * sin(6.28318 * (t + vec3(0.2, 0.5, 0.7))); 
}

float noiseStep(float x, float range) {
    const float offset = 0.25;
    float i = floor(x);
    float f = x - i;
    float u = smoothstep(0.5 - offset, 0.5 + offset, f);
    return (mix(floor(hash(i) * range), floor(hash(i + 1.0) * range), u) / (range - 1.0)) - 0.4;
}

vec3 motionOffset(vec3 position) {
    return vec3(
        noiseStep(position.y * 0.06, 6.0) * 2.5,
        noiseStep(position.z * 0.07, 4.0) * 2.0,
        noiseStep(position.x * 0.08, 5.0) * 1.2
    );
}

float triangleDist(vec2 p, float size) {
    return abs(p.x) + abs(p.y) - size * 1.1;
}

vec3 rotateVector(vec3 vec, vec3 axis, float angle) {
    return mix(dot(axis, vec) * axis, vec, cos(angle)) + cross(axis, vec) * sin(angle) * 1.1;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    vec3 color = vec3(0.0);
    vec3 rayOrigin = vec3(0.0, 0.0, -6.0), rayTarget = vec3(0.0);
    rayOrigin.z += iTime * 1.5;
    rayTarget.z += iTime * 1.5;
    rayOrigin += motionOffset(rayOrigin);
    rayTarget += motionOffset(rayTarget);
    vec3 direction = normalize(rayTarget - rayOrigin);
    vec3 right = vec3(direction.z, 0.0, -direction.x);
    float accumulated = 0.0, distance = 0.0;
    vec3 rayDir = mat3(right, cross(direction, right), direction) *
                   rotateVector(normalize(vec3(uv, 1.0)), vec3(0.0, 0.0, 1.0),
                   noiseStep(iTime + hash(uv.x * uv.y * iTime) * 0.2, 5.0));
    
    for (int i = 0; i < 80; i++) {
        float index = float(i) + 1.0;
        vec3 position = rayOrigin + rayDir * distance;
        position -= motionOffset(position);
        float light = 0.0;
        vec3 point = position;
        float scale = 1.0;
        for (int j = 0; j < 6; j++) {
            float jIndex = float(j) + 1.0;
            light += clamp(abs(dot(sin(point * 1.5), cos(point.yzx * 1.5)) * 0.7 - 0.15) / scale, -0.7, 0.7);
            point = rotateVector(point, normalize(vec3(0.5, 0.4, 0.2)), 0.8 + jIndex);
            point += point.yzx + jIndex * 10.0;
            scale *= 1.3;
            point *= 1.2;
        }
        float height = abs(triangleDist(position.xy, 12.0)) - 2.5 - light;
        position = rotateVector(position, vec3(0.0, 0.0, 1.0), motionOffset(position).x * 0.2 + position.z * 0.6);
        float thickness = length(abs(position.xy) - 0.25) - 0.07;
        height = min(thickness, height);
        float updateVal = max(0.001, thickness == height ? abs(height) : height);
        accumulated = updateVal;
        distance += updateVal;
        color += (thickness == height
                ? vec3(0.9, 0.4, 0.6) * (40.0 * exp(-8.0 * fract(position.z * 0.1 + iTime)))
                : vec3(0.03)) * 0.08 / exp(index * index * accumulated);
    }
    color = mix(color, vec3(1.0, 1.0, 0.9), 1.0 - exp(-0.02 * distance));
    fragColor = vec4(color, 1.0);
}

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