const float pi = 3.14159265358979323;
const float th = pi * 3. / 8.;
const float sc = 1. / cos(th);

vec2 intersectSphere(vec3 ro, vec3 rd, vec3 org, float rad)
{
    ro -= org;
    float a = dot(rd, rd);
    float b = 2. * dot(rd, ro);
    float c = dot(ro, ro) - rad * rad;
    float desc = b * b - 4. * a * c;
    if (desc < 0.)
        return vec2(1, 0);
	desc = sqrt(desc);
    return vec2(-b - desc, -b + desc) / (2. * a);
}

mat3 rotX(float a)
{
    return mat3(1., 0., 0., 0., cos(a), sin(a), 0., -sin(a), cos(a));
}

mat3 rotY(float a)
{
    return mat3(cos(a), 0., sin(a), 0., 1., 0., -sin(a), 0., cos(a));
}

mat3 rotZ(float a)
{
    return mat3(cos(a), sin(a), 0., -sin(a), cos(a), 0., 0., 0., 1.);
}

vec3 env(vec3 d)
{
    return vec3(.4);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord / iResolution.xy * 2. - 1.;
    uv.x *= iResolution.x / iResolution.y;

    vec3 ro = vec3(0, 0, 4.5), rd = normalize(vec3(uv, -4));


    vec2 t = intersectSphere(ro, rd, vec3(0), 1.);

    if(t.x > t.y)
    {
        fragColor.rgb = env(rd);
    }
    else
    {
        vec3 rp = ro + rd * t.x;
        vec3 rn = normalize(rp);

        vec3 c = vec3(0);
        float wsum = 0.;

        rp = rotY(iTime / 8.) * rp;
        for(int i = 0; i < 35; ++i)
        {
            vec3 d = normalize(textureLod(iChannel0, rp * .2, 0.).rgb - .5);
            d = rotZ(iTime * .1) * d;
            vec3 n = normalize(rp);
            d -= n * dot(n, d);
            rp += d * .011;
            float w = 1.;
            vec3 s;
            s = vec3(textureLod(iChannel0, rp * 1., 0.).a) * vec3(.3, 1., .7);
            s += vec3(textureLod(iChannel0, rp.zyx * 2.5, 0.).a) * vec3(1., .3, .7);
            c += s * w;
            wsum += w;

            rp = rotY(-.004) * rp;
        }

        rp = ro + rd * t.x;
        rp = rotY(iTime / 10.) * rp;
        for(int i = 0; i < 35; ++i)
        {
            vec3 d = normalize(textureLod(iChannel0, rp * .1, 0.).bgr - .5);
            d = rotZ(iTime * -.05) * d;
            vec3 n = normalize(rp);
            d -= n * dot(n, d);
            rp += d * .011;
            float w = .4;
            vec3 s;
            s = vec3(pow(textureLod(iChannel0, rp * 3., 0.).a, 2.)) * vec3(.3, 1., .7).bgr;
            c += s * w;
            wsum += w;
        }

        rp = ro + rd * t.x;
        rp = rotY(iTime / 12.) * rp;
        for(int i = 0; i < 35; ++i)
        {
            vec3 d = normalize(textureLod(iChannel0, rp * .1, 0.).brg - .5);
            d = rotY(iTime * -1.) * d;
            vec3 n = normalize(rp);
            d -= n * dot(n, d);
            rp += d * .01;
            float w = .2;
            vec3 s;
            s = vec3(pow(textureLod(iChannel0, rp * .5, 0.).a, 2.)) * vec3(0, 0, 1.);
            c += s * w;
            wsum += w;
        }

        c /= wsum;

        vec3 ld = normalize(vec3(1, 10.5, .8));

        c -= dot(c, vec3(1. / 3.)) * .7;
        c *= 4.5;

        c *= .5 + .5 * dot(rn, ld);

        vec3 refl = normalize(reflect(rd, rn));
        vec3 spec = vec3(pow(max(0., dot(refl, ld)), 8.)) * 0.1 + env(refl);
        spec += .5 * (1. - smoothstep(.25, .35, abs(refl.z))) *
            (1. - smoothstep(.8, .92, abs(refl.x))) * step(0., refl.y);
        spec += textureLod(iChannel1, refl, 4.).rgb * .2;
        c = mix(c, spec, .1 + pow(max(0., 1. - dot(rn, -rd)), 3.) * .8);

        fragColor.rgb = c;
    }

    fragColor.rgb *= 1.3;

    fragColor.rgb = pow(clamp(fragColor.rgb, 0., 1.), vec3(1. / 2.2));
}
