#define PI 3.1415926536
#define EPS 0.001

// https://www.shadertoy.com/view/4djSRW
float Hash21(in vec2 p) {
    vec3 p3 = fract(p.xyx * 0.1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}

void doTruchet(in vec2 uv, out vec2 cid, out vec2 cuv) {
    cid = floor(uv);
    cuv = fract(uv) - 0.5;
    if (Hash21(cid) < 0.5) cuv.x = -cuv.x;
    cuv -= cuv.x < -cuv.y ? -0.5 : 0.5;
}

float map(in vec3 p) {
    vec2 cid1, cuv1;
    doTruchet(p.xz, cid1, cuv1);
    float offs = 0.1 * sin(PI * (cuv1.x + cuv1.y)) * sin(PI * (cuv1.x - cuv1.y));
    float tru1 = length(vec2(length(cuv1) - 0.5, p.y + offs)) - 0.1;

    vec2 cid2, cuv2;
    doTruchet(p.xz - 10.5, cid2, cuv2);
    float tru2 = length(vec2(length(cuv2) - 0.5, p.y - offs)) - 0.1;

    return min(tru1, tru2);
}

vec3 getNormal(in vec3 p, in float d0) {
    return normalize(vec3(
        map(p + vec3(EPS, 0.0, 0.0)),
        map(p + vec3(0.0, EPS, 0.0)),
        map(p + vec3(0.0, 0.0, EPS))
    ) - d0);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y * 3.0;
    vec3 color = vec3(0.01);

    vec3 ro = vec3(cos(iTime * 0.25), 3.0, 3.0 * sin(iTime * 0.25));
    vec3 fd = -normalize(ro);
    vec3 rt = normalize(vec3(-ro.z, 0.0, ro.x));
    vec3 up = cross(fd, rt);
    vec3 rd = normalize(rt * uv.x + up * uv.y + fd * 4.0);
    ro.x += iTime;

    float t = 0.0, d;
    bool hit = false;
    vec3 p;
    for (int i = 0; i < 100; i++) {
        p = ro + rd * t;
        d = map(p);
        if (abs(d) < EPS) {
            hit = true;
            break;
        }

        if (t > 25.0) break;
        t += d;
    }

    if (hit) {
        vec3 normal = getNormal(p, d);
        color = 0.25 * pow(texture(iChannel0, reflect(rd, normal)).rgb, vec3(16.0));
    }

    fragColor = vec4(sqrt(color), 1.0);
}
