#define time iTime

const float PI = 3.14159265;

// noise background is baced on ???VIP:
// https://www.shadertoy.com/view/4sVBDm
// ---------------------------------------------------------------------

const float noiseIntensity = 2.8;
const float noiseDefinition = 0.6;
const vec2 glowPos = vec2(-2., 0.);

float random(vec2 co){
    return fract(sin(dot(co.xy ,vec2(-0.950,-0.910))) * 43758.5453);
}

float noise( in vec2 p ){
    p*=noiseIntensity;
    vec2 i = floor( p );
    vec2 f = fract( p );
	vec2 u = f*f*(3.0-2.0*f);
    return mix( mix( random( i + vec2(0.0,0.0) ), 
                     random( i + vec2(1.0,0.0) ), u.x),
                mix( random( i + vec2(0.0,1.0) ), 
                     random( i + vec2(1.0,1.0) ), u.x), u.y);
}

float fbm( in vec2 uv ){	
	uv *= 5.0;
    mat2 m = mat2( 1.6,  1.2, -1.2,  1.6 );
    float f  = 0.5*noise( uv ); uv = m*uv;
    f += 5.*noise( uv ); uv = m*uv;
    f += 5.*noise( uv ); uv = m*uv;    
	f = 0.1 + .5*f;
    return f;
}

vec3 bg(vec2 uv ){
    float velocity = time * 0.5;
    float intensity = sin(uv.x*noise(uv)*7.+velocity*3.)*noise(uv*10.)*1.0+.3;
    uv.y -= 2.;
    vec2 bp = uv+glowPos;
    uv *= noiseDefinition;

    //ripple
    float rb = fbm(vec2(uv.x*.5-velocity*.03, uv.y))*.1;
    uv += rb;

    //coloring
    float rz = fbm(uv*.9+vec2(velocity*.35, 0.0));
    rz *= dot(bp*intensity,bp);

    //bazooca line
    rz *= sin(uv.x*0.1+velocity*0.8);
    
    //lightning
    rz *= 5.*sin(uv.x*0.1+velocity*sin(time));
    
    vec3 bgColor = vec3(cos(time),0.45 * sin(time*0.2),sin(time*0.3));
    vec3 col = bgColor/(.1-rz);
    return sqrt(abs(col));
}

// IFS MengerSponge based on gam0022.net
// https://gam0022.net/blog/2019/06/25/unity-raymarching/
// ---------------------------------------------------------------------
#define ITERATIONS 4

vec3 _MengerOffset = vec3(1.000,0.072,0.414);
float _MengerScale = 2.9;
float _MengerFold = 3.;

// IFS MengerSponge DistanceFunction
float dMenger(vec3 z0, vec3 offset, float scale) {
    vec4 z = vec4(z0, 1.0);
    for (int n = 0; n < ITERATIONS; n++) {
        z = abs(z);

        if (z.x < z.y) z.xy = z.yx;
        if (z.x < z.z) z.xz = z.zx;
        if (z.y < z.z) z.yz = z.zy;

        z *= scale;
        z.xyz -= offset * (scale - 1.0);

        if (z.z < -0.5 * offset.z * (scale - 1.0))
            z.z += offset.z * (scale - 1.0);
    }
    return (length(max(abs(z.xyz) - vec3(1.0, 1.0, 1.0), 0.0)) - 0.05) / z.w;
}

// 2D rotate
mat2 rotate(in float a) {
    float s = sin(a), c = cos(a);
    return mat2(c, s, -s, c);
}

// Deformation used folding
// https://www.shadertoy.com/view/Mlf3Wj
vec2 foldRotate(in vec2 p, in float s) {
    float a = PI / s - atan(p.x, p.y);
    float n = PI*2. / s;
    a = floor(a / n) * n;
    p *= rotate(a);
    return p;
}

// Final DistanceFunction
float map(vec3 pos) {
    pos.xy = foldRotate(pos.xy, _MengerFold);
    pos.yz = foldRotate(pos.yz, _MengerFold);
    return dMenger(pos, _MengerOffset, _MengerScale);
}

// Normal
vec3 getNormal(vec3 p){
    const vec2 e = vec2(0.001, 0.0);
	return normalize(vec3(
		map(p + e.xyy) - map(p - e.xyy),
		map(p + e.yxy) - map(p - e.yxy),
		map(p + e.yyx) - map(p - e.yyx)));
}
// ---------------------------------------------------------------------
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 p = (fragCoord.xy * 2.0 - iResolution.xy) / min(iResolution.x, iResolution.y);
    vec2 uv = (fragCoord.xy * 2.0 - iResolution.xy) / iResolution.y;// background
    
    // camera
    float pscale = 1.0 + 0.85*cos(time*0.5);
    vec3 cPos = vec3(pscale*cos(time*0.5), 0.0, pscale*sin(time*0.5));
    vec3 cDir = vec3(1.*sin(time*0.5+PI*1.5), 0.0, -1.*cos(time*0.5+PI*1.5));
    vec3 cUp  = vec3(0.0, 1.0, 0.0);
    vec3 cSide = cross(cDir, cUp);
    float targetDepth = 1.0;
     
    // ray
    vec3 rPos = cPos;// cPos = ray origin
    vec3 rd = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);// ray direction
    
    // marching loop
    float dist = 0.0;
    float rLen = 0.0;
    for(int i = 0; i < 42; i++){
        dist = map(rPos);
        rLen += dist;
        rPos = cPos + rd * rLen;
    }
    
    //color
    vec3 color = vec3(0.0);
    vec3 bgCol = bg(p)*(2.-abs(uv.y*2.));// background color
    
    // hit check
    if(abs(dist) < 0.001){
        vec3 normal = getNormal(rPos);
        vec3 refDir = reflect(rd, normal);// reflect direction
        float refInt = max(0.0, dot(refDir, vec3(0.279,0.284,0.315)));// reflect Intensity
        vec3 refCol = 30.0*pow(refInt, 1.5)*(vec3(0.285,0.230,0.211));// reflect color
        color = mix(bgCol, refCol, 0.5);
        color = mix(color, refDir, 0.1);
    }else{
        color = bg(p*rotate(PI*0.5))*bgCol*0.1;
    }
    
	fragColor = vec4(mix(bgCol, color, 0.65-0.5*(sin(cos(time*0.5-150.))-0.2)), 1.0);
}
