//////////////////////////////////////////////////////////////////////////////// // // Torus knot (p,p+1), inverted (to make a Dupin cyclide knot I guess). // // Colouring from tdhoopers "Shaderdough Fairy" // // https://www.shadertoy.com/view/4lGyW1 // // mouse/up/down: change view // i: inversion // left/right: knot number (0 to cycle). // //////////////////////////////////////////////////////////////////////////////// vec2 thetorus = vec2(1.0, 0.7); bool doinvert = false; // "Fairy" coloring lifted from tdhooper's ... const float MAX_TRACE_DISTANCE = 10.0; const float INTERSECTION_PRECISION = 0.001; #ifdef HIGH_QUALITY const float FUDGE_FACTOR = .2; #else const float FUDGE_FACTOR = .4; #endif const float PI = 3.14159; bool dorotate = true; float scale = 1.0; vec2 rotate(vec2 p, float t) { return p * cos(t) + vec2(p.y, -p.x) * sin(t); } float A = 10.0; float B = 11.0; float torus(in vec3 p, in vec2 t, out vec2 uv) { float rho = atan(A/B); float theta = atan(p.y,p.x); float phi = atan(p.z,length(p.xy)-t.x); uv = vec2(theta,phi); uv /= 2.0*PI; uv = rotate(uv,rho); uv += 0.2*iTime; float K = sqrt(A*A+B*B); vec2 grid = vec2(K); uv *= grid; uv.y = round(uv.y); uv /= grid; uv -= 0.2*iTime; uv = rotate(uv,-rho); theta = 2.0*PI*uv.x; phi = 2.0*PI*uv.y; vec2 rad = vec2(cos(theta),sin(theta)); // Unit length radial vector vec3 r = vec3(t.x*rad+t.y*cos(phi)*rad,t.y*sin(phi)); return distance(p,r)-0.1; } vec3 invert(vec3 p, vec3 centre, float r2, inout float k) { p -= centre; float r = dot(p,p); p *= r2/r; p += centre; k *= r/r2; return p; } float map(in vec3 p, out vec2 uv) { float k = 1.0; if (doinvert) { vec3 centre = vec3(sin(0.5*iTime)); p = invert(p,centre,1.0,k); } float d = torus(p,thetorus,uv); float res = k*d; return res; } vec3 transform(in vec3 p) { if (iMouse.x > 0.0) { float theta = (2.0*iMouse.y-iResolution.y)/iResolution.y*PI; float phi = (2.0*iMouse.x-iResolution.x)/iResolution.x*PI; p.yz = rotate(p.yz,-theta); p.zx = rotate(p.zx,phi); } if (dorotate) { p.yz = rotate(p.yz,iTime * 0.125); p.zx = rotate(p.zx,iTime * 0.2); } return p; } // Smooth hue to RGB conversion // Function by iq, from https://www.shadertoy.com/view/MsS3Wc vec3 h2rgb(float hue) { vec3 rgb = clamp( abs(mod(hue*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0 ); return rgb*(3.0-2.0*rgb); // cubic smoothing } bool keypress(int code) { return texelFetch(iChannel0, ivec2(code,2),0).x != 0.0; } vec4 store(int i,int j) { return texelFetch(iChannel1, ivec2(i,j),0); } int keycount(int key) { return int(store(0,key).x); } const int CHAR_I = 73; const int KEY_LEFT = 37; const int KEY_UP = 38; const int KEY_RIGHT = 39; const int KEY_DOWN = 40; void mainImage( out vec4 fragColor, in vec2 fragCoord ) { doinvert = !keypress(CHAR_I); vec2 uv = scale*(2.0*fragCoord.xy - iResolution.xy)/iResolution.y; vec3 ro = vec3(0,0,4); ro *= 0.1*float(10+keycount(KEY_DOWN)-keycount(KEY_UP)); vec3 rd = normalize(vec3(uv,-2)); A = max(0.0,float(keycount(KEY_RIGHT)-keycount(KEY_LEFT))); if (A == 0.0) { A = floor(8.0-4.0*cos(0.2*iTime)); } B = A+1.0; ro = transform(ro); rd = transform(rd); rd = normalize(rd); vec3 col = pow(vec3(.15,0,.2), vec3(2.2)); int iter = int(20. / FUDGE_FACTOR); float t = 0.0; for( int i=0; i < iter; i++ ){ if( t > MAX_TRACE_DISTANCE ) break; float d = map( ro+rd*t, uv ); vec3 c = h2rgb(uv.x+uv.y); float h = abs(d); t += max(INTERSECTION_PRECISION, h * FUDGE_FACTOR); col += c * pow(max(0., (.02 - h)) * 19.5, 10.) * 150.; col += c * .001 * FUDGE_FACTOR; } col = pow(col, vec3(1./1.8)) * 1.5; col = pow(col, vec3(1.5)); col *= 3.0; fragColor = vec4(col,1.0); }