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

uniform vec3 iResolution;
uniform float iTime;
uniform float uAlpha;
uniform float uTimeScale;
uniform float uValue;
uniform int uRaySteps;
uniform float uHitEpsilon;
uniform float uStepScale;
uniform float uFov;
uniform vec2 uScreenScale;
uniform float uGroundHeight;
uniform float uCameraHeight;
uniform float uCameraDepthOffset;
uniform float uColorDistanceRed;
uniform float uColorDistanceGreen;
uniform float uColorDistanceBlue;
uniform float uFogAmount;
uniform vec3 uBaseTint;

const float PI = 3.14159265358979323846;

float rand(in vec2 p, in float t)
{
    return fract(sin(dot(p + mod(t, 1.0), vec2(12.9898, 78.2333))) * 43758.5453);
}

vec2 rotate(vec2 k, float t)
{
    return vec2(cos(t) * k.x - sin(t) * k.y, sin(t) * k.x + cos(t) * k.y);
}

float scene1(vec3 p)
{
    float speed = iTime * uTimeScale;
    float ground = dot(p, vec3(0.0, 1.0, 0.0)) + uGroundHeight;
    float t1 = length(abs(mod(p.xyz, 2.0) - 1.0)) - 1.35 + 0.05 * cos(PI * p.x * 4.0) + 0.05 * sin(PI * p.z * 4.0);
    float t3 = length(max(abs(mod(p.xyz, 2.0) - 1.0).xz - 1.0, 0.5)) - 0.075 + 0.1 * cos(p.y * 36.0);
    float t5 = length(abs(mod(p.xyz, 0.5)) - 0.25) - 0.975;
    float bubbleW = 0.8 + 0.2 * cos(PI * p.z) + 0.2 * cos(PI * p.x);
    float bubble = length(mod(p.xyz, 0.125) - 0.0625) - bubbleW;
    float holeW = 0.05;
    float hole = length(abs(mod(p.xz, 1.0) - 0.5)) - holeW;
    float tubeP = 2.0 - 0.25 * sin(PI * p.z * 0.5);
    float tubeV = PI * 8.0;
    float tubeB = tubeP * 0.02;
    float tubeW = tubeB + tubeB * cos(p.x * tubeV) * sin(p.y * tubeV) * cos(p.z * tubeV) + tubeB * sin(PI * p.z + speed * 4.0);
    float tube = length(abs(mod(p.xy, tubeP) - tubeP * 0.5)) - tubeW;
    return min(max(min(-t1, max(-hole - t5 * 0.375, ground + bubble)), t3 + t5), tube);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    float speed = iTime * uTimeScale;
    float groundX = 1.5 * cos(PI * speed * 0.125);
    float groundY = uCameraHeight - 3.0 * sin(PI * speed * 0.125) + 0.125 * uValue;
    float groundZ = uCameraDepthOffset - speed;
    vec2 position = fragCoord.xy / iResolution.xy;
    vec2 p = -1.0 + 2.0 * position;

    vec3 dir = normalize(vec3(p * uScreenScale, uFov));
    dir.yz = rotate(dir.yz, PI * 0.25 * sin(PI * speed * 0.125) - uValue * 0.25);
    dir.zx = rotate(dir.zx, PI * cos(-PI * speed * 0.05));
    dir.xy = rotate(dir.xy, PI * 0.125 * cos(PI * speed * 0.125));

    vec3 ray = vec3(groundX, groundY, groundZ);
    float t = 0.0;

    for (int i = 0; i < 160; i++)
    {
        if (i >= uRaySteps)
            break;

        float k = scene1(ray + dir * t);
        if (abs(k) < uHitEpsilon)
            break;

        t += k * uStepScale;
    }

    vec3 hit = ray + dir * t;
    vec2 h = vec2(-0.02, 0.01);
    vec3 n = normalize(vec3(scene1(hit + h.xyy), scene1(hit + h.yxx), scene1(hit + h.yyx)));
    float c = (n.x + n.y + n.z) * 0.1;
    vec3 color = vec3(c) - t * uFogAmount;
    vec3 structureColor = vec3(c + t * uColorDistanceRed, c + t * uColorDistanceGreen, c * 1.5 - t * uColorDistanceBlue);
    fragColor = vec4((structureColor + color * color) * uBaseTint, uAlpha);
}

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