#define PI 3.1415
#define FAR 300.0
#define MAXSTEP 250
#define EPS 0.001

float distToTorus(vec3 p) {
    float mul = 7.2;
    vec3 pm = mod(p, mul)-0.5*mul;
    vec3 pd = p - mod(p,mul);
    float a = iTime*0.5 + pd.z*1.7 + pd.y*0.79 + pd.x;
    pm =
    mat3(
        cos(a), 0.0, -sin(a),
        0.0   , 1.0, 0.0,
        sin(a), 0.0, cos(a)
    )*
    mat3(
        1.0   , 0.0   ,  0.0,
        0.0   , cos(a), -sin(a),
        0.0   , sin(a), cos(a)
    )*
    pm;

    // https://iquilezles.org/articles/distfunctions
    vec2 t = vec2(1.0, 0.4);
    vec2 q = vec2(length(pm.xy)-t.x,pm.z);
  	return length(q)-t.y;
}

vec3 getRayForPixel(vec2 p, float vAngle) {
    vec2 uv = p - 0.5*iResolution.xy;
    float cameraDistToScreen = 0.5*iResolution.y / tan(vAngle/2.0);
    return normalize(vec3(uv, -cameraDistToScreen));
}

float castRay(vec3 eye, vec3 ray) {
    float length = 0.0;
    for (int i=0; i<MAXSTEP; i++) {
        float dist = distToTorus(eye+length*ray);
        if (dist<=EPS) {
            return length;
        }
        if (length>=FAR) {
            return FAR;
        }
        length += dist*0.8;
    }
    return FAR;
}

vec3 getNormal(vec3 p) {
    return normalize(vec3(
       distToTorus(p+vec3(EPS,0.0,0.0)) - distToTorus(p-vec3(EPS,0.0,0.0)),
       distToTorus(p+vec3(0.0,EPS,0.0)) - distToTorus(p-vec3(0.0,EPS,0.0)),
       distToTorus(p+vec3(0.0,0.0,EPS)) - distToTorus(p-vec3(0.0,0.0,EPS))

    ));
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec3 eye = vec3(0.0, iTime, mod(-2.0*iTime,-2048.0));
    vec3 ray = getRayForPixel(fragCoord, PI/7.0);
    float a = 2.0*PI * (iMouse.x-0.5*iResolution.x) / iResolution.x;
    float b = 2.0*PI * (iMouse.y-0.5*iResolution.y) / iResolution.y;
    ray = mat3(
        1.0, 0.0   , 0.0,
        0.0, cos(b), -sin(b),
        0.0, sin(b), cos(b)
    ) * mat3(
        cos(a), 0.0, -sin(a),
        0.0   , 1.0, 0.0,
        sin(a), 0.0, cos(a)
    )  * ray;
    float len = castRay(eye, ray);
    vec4 fogColor = vec4(0.75, 0.9, 1.0, 1.0);
    float fogAmount = len/FAR;
    fragColor = mix(texture(iChannel0, reflect(ray, getNormal(eye+len*ray))), fogColor, fogAmount);
}