// COLORS
// src : nvidia
float saturate(float x)
{
    return max(0.0, min(1.0, x));
}


vec3 saturate(vec3 v)
{
    return vec3(
        max(0.0, min(1.0, v.x)),
        max(0.0, min(1.0, v.y)),
        max(0.0, min(1.0, v.z))
        );
}

// src reefwirrax
vec3 Hue(float h)
{
    float r = abs(h*6.0-3.0)-1.0;
    float g = 2.0 - abs(h*6.0-2.0);
    float b = 2.0 - abs(h*6.0-4.0);
    return saturate(vec3(r,g,b));
}

vec3 HSVtoRGB(vec3 hsv)
{
    return vec3(((Hue(hsv.x) - 1.0) * hsv.y + 1.0) * hsv.z);
}

// world
// src : Fabrice Castel
float DistanceToNearestSurface(vec3 p)
{
    float t;
    p.y += sin(p.z * 0.25 + iTime * 5. + sin(p.x + iTime * 10.));
    
    vec3 q = vec3(mod(p.x, 3.0) - 1.5, p.y, mod(p.z, 3.0) - 1.5);
    float s = 1.0;
    vec3 d = abs(q) - vec3(s);
    
    t = min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
    
    return t;
}

float castRay(vec3 rayOrigin, vec3 rayDir)
{
    float t;
    float anim = fract(iTime);
    
    for(int i=0; i<128 ; i++)
    {
        float res = 0.0;
        vec3 march = rayOrigin + rayDir * t;
        res = DistanceToNearestSurface(march);
        
        if(res < (0.0001*t))
        {
            return t;
        }
        t += res;
    }
    
    return -1.0;
}

vec2 NormalizeScreen(vec2 st)
{
    vec2 uv = (2.0 * st/iResolution.xy - 1.0);
    uv.x *= iResolution.x/iResolution.y;
    return uv;
}

vec3 render(vec2 uv, vec3 rayOrigin, vec3 rayDir)
{
    vec3 col;
    float t = castRay(rayOrigin, rayDir);
    float ao = pow(1.-t * 0.005, 1.);
    vec3 rainbow = vec3(HSVtoRGB(vec3(fract(ao+iTime*.5), 1.0, 1.0)));
    
    if(t == -1.0)
    {
        vec2 st = uv;
        float angle = fract(atan(st.x, st.y)*5.);
        float sun = smoothstep(.3, .31, length(st));
        sun += step(.8, fract(st.y*10.-.3));
        
        float at = angle+iTime;
        vec3 yolo = rainbow * (smoothstep(.1, .3, fract(at))-smoothstep(.3, .5, fract(at)));
        
        col = mix(
            vec3(max(mix(vec3(.0), yolo, smoothstep(.29, .30, length(st))), mix(rainbow, vec3(.0), sun))), // 
            vec3(.0),
            vec3(step(0.03, rayDir.y))
            );
    }
    else
    {
        col = rainbow;
    }
    
    return col;
}

vec3 CameraViewDir(vec2 uv, vec3 camPos, vec3 camTarget)
{
    vec3 viewDir;
    vec3 forward = normalize(camTarget - camPos);
    vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), forward));
    vec3 up = normalize(cross(right, forward));
    float fPersp = 4.0;
    
    viewDir = normalize(uv.x * right + uv.y * up + fPersp * forward);
    
    return viewDir;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec3 col = vec3(.5);
    float offsetTime = iTime+1000.;
    vec3 camPos = vec3(sin(offsetTime)*5., -5., offsetTime);
    vec3 camTarget = vec3(0.0, 0.0, 0.0);
    
    vec2 uv = NormalizeScreen(fragCoord);
    vec3 viewDir = CameraViewDir(uv, camPos, camTarget);
    
    col = vec3(length(uv)-.5);
    
    col = render(uv, camPos, viewDir);
    
    fragColor = vec4(col, 1.0);
}
