/*{ "DESCRIPTION": "A mid-1990s raytracer showpiece at sunset, traced for real. A chrome sphere and a refracting glass sphere preside over a grid of dead beige CRT terminals whose phosphor screens run little demoscene programs - source code typed out character by character, plasma, a checkered tunnel, digital rain, and one showing a live view of this very scene - all mirrored in a cracked, polished veined-marble checkerboard. Beside them stands a ruined Greek arcade: drum-built columns carrying real masonry arches, each a semicircular void cut through a stone spandrel by CSG. Shark fins cut the glittering lake, eyeballs drift overhead, and an Earth hangs in a sky that runs from a molten orange horizon through violet to a black star field. Whitted-style recursive reflections and refraction, one low warm key light with long hard shadows and cool skylight fill. Sliders: orbit, lens, camera height, eyeballs, eye speed, screen glow, sky hue, water hue, waves, reflection, palette depth, dither, scanlines, vignette, sky-art (paints the terminals' programs across the sky).", "CREDIT": "CC0", "CATEGORIES": ["generator", "raytracing", "retro", "3d", "landscape", "sunset"], "INPUTS": [ { "NAME": "uSpeed", "TYPE": "float", "DEFAULT": 1.00, "MIN": 0.0, "MAX": 3.0 }, { "NAME": "uFOV", "TYPE": "float", "DEFAULT": 2.10, "MIN": 1.1, "MAX": 4.0 }, { "NAME": "uCamHt", "TYPE": "float", "DEFAULT": 3.20, "MIN": 0.6, "MAX": 9.0 }, { "NAME": "uEyes", "TYPE": "float", "DEFAULT": 6.00, "MIN": 0.0, "MAX": 10.0 }, { "NAME": "uEyeSpeed", "TYPE": "float", "DEFAULT": 1.00, "MIN": 0.0, "MAX": 3.0 }, { "NAME": "uScreens", "TYPE": "float", "DEFAULT": 1.25, "MIN": 0.0, "MAX": 3.0 }, { "NAME": "uSkyHue", "TYPE": "float", "DEFAULT": 0.00, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "uWaveHue", "TYPE": "float", "DEFAULT": 0.00, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "uWave", "TYPE": "float", "DEFAULT": 1.00, "MIN": 0.0, "MAX": 2.0 }, { "NAME": "uGloss", "TYPE": "float", "DEFAULT": 0.90, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "uPalette", "TYPE": "float", "DEFAULT": 32.0, "MIN": 2.0, "MAX": 32.0 }, { "NAME": "uDither", "TYPE": "float", "DEFAULT": 0.12, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "uCRT", "TYPE": "float", "DEFAULT": 0.06, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "uVignette", "TYPE": "float", "DEFAULT": 0.25, "MIN": 0.0, "MAX": 1.0 }, { "NAME": "uSkyArt", "TYPE": "float", "DEFAULT": 0.00, "MIN": 0.0, "MAX": 1.0 } ] }*/ // RAYTRACE RELIC — glslop edition. // A compact rebuild of a much larger scene shader: the full version runs // 49 hand-placed terminals and ~20 materials, which compiles to far more // code than a software rasteriser will accept. Here every repeated thing // (terminals, columns, arches, fins, eyes) is placed by MATH inside a // loop instead of being unrolled by hand, which keeps the program small // enough to compile anywhere while keeping the scene's identity. // GLSL ES 1.00: constant loop bounds, gl_FragColor, TIME / RENDERSIZE. #define PLAZA 8.0 #define TAU 6.28318 vec3 LD; // toward the low sun float gT; // ---- hashes / noise ----------------------------------------- float h21(vec2 p){ p = fract(p * vec2(123.34, 456.21)); p += dot(p, p + 45.32); return fract(p.x * p.y); } float vnoise(vec2 p){ vec2 i = floor(p), f = fract(p); f = f*f*(3.0 - 2.0*f); return mix(mix(h21(i), h21(i + vec2(1.0, 0.0)), f.x), mix(h21(i + vec2(0.0, 1.0)), h21(i + vec2(1.0, 1.0)), f.x), f.y); } float fbm(vec2 p){ float v = 0.0, a = 0.5; for (int i = 0; i < 3; i++){ v += a*vnoise(p); p *= 2.03; a *= 0.5; } return v; } vec3 hueShift(vec3 c, float a){ if (a < 0.001) return c; vec3 k = vec3(0.57735); float ca = cos(a*TAU), sa = sin(a*TAU); return c*ca + cross(k, c)*sa + k*dot(k, c)*(1.0 - ca); } float bayer(vec2 a){ a = floor(a); float b = fract(a.x*0.5 + a.y*a.y*0.75); vec2 c = floor(0.5*a); return fract(c.x*0.5 + c.y*c.y*0.75)*0.25 + b; } // ---- sky: sunset -> violet -> starfield, clouds, mountains --- vec3 skyCol(vec3 rd){ float h = clamp(rd.y, 0.0, 1.0); vec3 c = mix(vec3(1.00, 0.45, 0.15), vec3(0.40, 0.20, 0.42), smoothstep(0.0, 0.20, h)); c = mix(c, vec3(0.02, 0.03, 0.11), smoothstep(0.16, 0.72, h)); float sd = max(dot(rd, LD), 0.0); c += vec3(1.00, 0.50, 0.18) * pow(sd, 10.0) * 0.60; c += vec3(1.00, 0.88, 0.66) * pow(sd, 900.0) * 7.0; float sf = smoothstep(0.22, 0.70, h); if (sf > 0.01) c += vec3(0.90, 0.94, 1.0) * smoothstep(0.9955, 0.9990, h21(floor(rd.xz/max(rd.y,0.2)*110.0))) * sf * 1.6; float hz = smoothstep(0.02, 0.16, rd.y); if (hz > 0.002){ float f = fbm(rd.xz/max(rd.y, 0.02)*0.55 + vec2(gT*0.008, gT*0.005)); vec3 lit = mix(vec3(0.30, 0.18, 0.30), vec3(1.00, 0.74, 0.48), smoothstep(0.40, 0.80, f)); lit = mix(lit, vec3(1.00, 0.42, 0.14), smoothstep(0.32, 0.03, rd.y)); c = mix(c, lit, smoothstep(0.42, 0.70, f)*hz*0.95); } // an Earth, as a lit disc in a tangent basis (no geometry) vec3 pd = normalize(vec3(0.62, 0.34, -0.71)); vec3 T = normalize(cross(pd, vec3(0.0, 1.0, 0.0))), B = cross(T, pd); vec2 q = vec2(dot(rd, T), dot(rd, B)) / 0.20; float r2 = dot(q, q); if (r2 < 1.0){ vec3 pn = normalize(T*q.x + B*q.y + pd*sqrt(1.0 - r2)); float land = fbm(q*1.9 + 5.0); vec3 pc = mix(vec3(0.05, 0.16, 0.42), vec3(0.10, 0.30, 0.55), smoothstep(0.38, 0.48, land)); pc = mix(pc, mix(vec3(0.22, 0.38, 0.16), vec3(0.55, 0.46, 0.28), smoothstep(0.52, 0.68, land)), smoothstep(0.48, 0.52, land)); pc = mix(pc, vec3(0.95, 0.96, 1.0), smoothstep(0.52, 0.70, fbm(q*2.6 - 2.0))*0.75); c = pc*(0.10 + 0.90*max(dot(pn, LD), 0.0)) + vec3(0.35, 0.55, 0.95)*pow(r2, 6.0)*0.35; } float sl = rd.y / max(length(rd.xz), 1e-4); if (sl < 0.40){ float az = atan(rd.x, rd.z); float h2 = max(0.085 + 0.075*sin(az*3.0 + 2.1) + 0.045*sin(az*7.3 + 0.6), 0.02); float h1 = 0.075 + 0.110*sin(az*2.2 + 5.0) + 0.060*sin(az*5.9 + 1.7); if (sl < h2) c = mix(vec3(0.42, 0.24, 0.30), vec3(0.30, 0.20, 0.34), clamp(sl/h2, 0.0, 1.0)); if (sl < h1) c = mix(mix(vec3(0.16, 0.07, 0.10), vec3(0.10, 0.07, 0.14), clamp(sl*4.0, 0.0, 1.0)), vec3(1.0, 0.78, 0.60), smoothstep(0.07, 0.14, sl)); } return hueShift(c, uSkyHue); } // a low-bit "camera feed" of this same world, for one of the screens vec3 sceneFeed(vec2 uv){ vec2 q = uv*2.0 - 1.0; float a = gT*0.15; vec3 ro = vec3(sin(a)*13.0, 3.4, cos(a)*13.0); vec3 fw = normalize(vec3(0.0, 0.9, 0.0) - ro); vec3 rt = normalize(cross(fw, vec3(0.0, 1.0, 0.0))); vec3 rd = normalize(fw*1.55 + rt*q.x + cross(rt, fw)*q.y*0.62); vec3 c; if (rd.y < -0.001){ float d = -ro.y/rd.y; vec3 p = ro + rd*d; float e = max(abs(p.x), abs(p.z)); if (e < PLAZA) c = mix(vec3(0.20, 0.11, 0.11), vec3(0.90, 0.86, 0.80), mod(floor(p.x) + floor(p.z), 2.0)); else c = mix(vec3(0.72, 0.64, 0.46), vec3(0.02, 0.09, 0.15), smoothstep(PLAZA, PLAZA + 3.2, e)); c *= clamp(15.0/d, 0.20, 1.0); } else c = skyCol(rd); return floor(c*7.0 + 0.5)/7.0 * 1.2; } // ---- what each terminal is running -------------------------- vec3 screenArt(vec2 uv, float seed){ float sd = fract(sin(seed*12.9898 + 4.1)*43758.5453); if (seed > 90.0) return sceneFeed(uv); // the pillar-top set // three independent hashes per machine: which program it runs, and // then its own zoom, speed and palette rotation — so two terminals on // the same program still don't look like each other float h2 = fract(sin(seed*78.233 + 1.7)*24634.633); float h3 = fract(sin(seed*41.317 + 9.2)*17231.117); float zoom = 0.65 + 1.10*h2; float rate = 0.55 + 1.30*h3; float t = (gT + seed*3.7)*rate; // program picked by INDEX, not by hash: with 25 machines a hash // leaves some programs unseen, and the fractal was one of the missing // ones. mod() cycles through all eight so each is on screen somewhere. float prog = mod(seed, 8.0); vec3 ph = (sd < 0.34) ? vec3(0.34, 1.00, 0.42) : (sd < 0.67) ? vec3(1.00, 0.70, 0.20) : vec3(0.60, 0.80, 1.00); vec2 q = uv*2.0 - 1.0; vec3 c; if (prog < 1.0){ // source code, typed out fast, scrolling up float S = 6.5 + 2.0*sd, scr = t*S + sd*80.0; float row = floor((1.0 - uv.y)*17.0 + scr), col = floor(uv.x*30.0); float ind = floor(h21(vec2(row, 1.7))*5.0)*2.0; float len = 5.0 + h21(vec2(row, 4.2))*19.0; float lc = col - ind; float on = step(0.0, lc)*step(lc, len) * step(0.22, h21(vec2(floor(lc*0.25), row))) * step(0.18, h21(vec2(col, row))); float typed = fract(scr)*(len + 3.0); if (abs(row - floor(scr)) < 0.5) on *= step(lc, typed); float cur = (abs(row - floor(scr)) < 0.5 && abs(lc - typed) < 0.9) ? 1.0 : 0.0; float tone = 0.55 + 0.65*h21(vec2(col*3.1, row*1.3)); if (h21(vec2(row, 9.9)) < 0.14) tone *= 0.45; // comment line c = ph*(0.10 + on*tone*2.0) + vec3(1.0)*cur*1.8; } else if (prog < 2.0){ vec2 g = uv*6.0*zoom; float v = sin(g.x + t*1.7) + sin(g.y + t*1.3) + sin((g.x + g.y)*0.7 + t*2.1) + sin(length(g - 3.0)*2.0 - t*2.4); c = (0.5 + 0.5*cos(TAU*(vec3(0.0, 0.33, 0.67) + v*0.25 + sd)))*1.25; } else if (prog < 3.0){ float r = max(length(q), 1e-3); float ck = mod(floor(atan(q.y, q.x)/3.14159*9.0) + floor((0.35/r + t*0.8)*3.0), 2.0); c = mix(vec3(0.04, 0.02, 0.12), ph, ck) * clamp(r*1.9, 0.0, 1.0); } else if (prog < 4.0){ // digital rain float cx = floor(uv.x*(10.0 + 12.0*h2)); float head = fract(h21(vec2(cx, 5.0)) + t*(0.6 + h21(vec2(cx, 1.0))*1.6)*0.4); float rw = floor((1.0 - uv.y)*20.0); c = ph * step(0.35, h21(vec2(cx, rw + floor(t*9.0)))) * smoothstep(0.5, 0.0, fract(head - rw/20.0)) * 1.6; } else if (prog < 5.0){ // oscilloscope over a graticule float w = 0.55*sin(q.x*(4.0 + 5.0*h2) + t*5.0)*sin(q.x*2.0 - t*1.3); vec2 g = abs(fract(uv*8.0) - 0.5); c = ph*0.22*smoothstep(0.46, 0.5, max(g.x, g.y)) + ph*smoothstep(0.10, 0.0, abs(q.y - w))*1.7; } else if (prog < 6.0){ // metaballs float fsum = 0.0; for (int k = 0; k < 3; k++){ float fk = float(k); vec2 bp = 0.6*vec2(sin(t*(0.7 + 0.3*fk) + fk*2.0), cos(t*(0.5 + 0.4*fk) + fk)); fsum += 0.14/max(dot(q - bp, q - bp), 0.01); } c = mix(vec3(0.01, 0.0, 0.04), ph*1.25, smoothstep(1.0, 1.9, fsum)); } else if (prog < 7.0){ // concentric interference rings float d1 = length(q - vec2(0.35*sin(t*0.7), 0.30*cos(t*0.9))); float d2 = length(q + vec2(0.32*cos(t*0.6), 0.28*sin(t*1.1))); float w = sin(d1*(14.0*zoom) - t*3.0) * sin(d2*(12.0*zoom) + t*2.2); c = ph * smoothstep(0.12, 0.92, 0.5 + 0.5*w) * 1.35 * (0.45 + 0.55*smoothstep(1.3, 0.1, length(q))); } else { // the fractal: a Julia set with its seed orbiting vec2 z = q*(1.45/zoom); vec2 k = 0.7885*vec2(cos(t*0.35), sin(t*0.35)); float it = 0.0; for (int i = 0; i < 16; i++){ if (dot(z, z) < 4.0){ z = vec2(z.x*z.x - z.y*z.y, 2.0*z.x*z.y) + k; it += 1.0; } } float fr = it/16.0; c = (0.5 + 0.5*cos(TAU*(vec3(0.0, 0.40, 0.70) + fr*2.5 + sd)))*step(fr, 0.999); } // per-machine palette rotation on top of the phosphor choice c = hueShift(c, h3*0.16); return c; } // Eye positions. ring 0 = the big outer drifters, ring 1 = a tighter // swarm of small ones orbiting close over the chrome ball. vec3 eyePos(float fe, float ring){ float a1 = h21(vec2(fe, 1.3 + ring*5.0)); float a2 = h21(vec2(fe, 7.7 + ring*3.0)); float sp = uEyeSpeed*(0.25 + 0.60*a1); float an = gT*sp*(0.45 + ring*0.55) + fe*2.39 + ring*1.1; float rr = (ring < 0.5) ? (3.0 + 5.6*a2 + 0.85*sin(gT*sp*0.31 + fe*1.9)) : (2.9 + 1.5*a2 + 0.40*sin(gT*sp*0.60 + fe*2.3)); float hy = (ring < 0.5) ? (6.20 + 2.4*a1 + 0.75*sin(gT*sp*0.90 + fe*1.7)) : (4.05 + 0.9*a1 + 0.45*sin(gT*sp*1.20 + fe*2.1)); return vec3(cos(an)*rr + 0.55*sin(gT*sp*0.53 + fe*2.7), hy, sin(an)*rr + 0.55*cos(gT*sp*0.47 + fe*3.3)); } float eyeRad(float fe, float ring){ return (ring < 0.5) ? (0.26 + 0.24*h21(vec2(fe, 7.7))) : (0.13 + 0.10*h21(vec2(fe, 2.4))); } // ---- primitives --------------------------------------------- float iSph(vec3 ro, vec3 rd, vec3 c, float r){ vec3 oc = ro - c; float b = dot(oc, rd), h = b*b - dot(oc, oc) + r*r; if (h < 0.0) return -1.0; h = sqrt(h); float t = -b - h; if (t < 0.001) t = -b + h; return t > 0.001 ? t : -1.0; } float iBox(vec3 ro, vec3 rd, vec3 c, vec3 b, out vec3 n){ vec3 o = ro - c, m = 1.0/rd, k = abs(m)*b, t1 = -m*o - k, t2 = -m*o + k; float tN = max(max(t1.x, t1.y), t1.z), tF = min(min(t2.x, t2.y), t2.z); n = vec3(0.0); if (tN > tF || tF < 0.001) return -1.0; n = -sign(rd)*step(t1.yzx, t1.xyz)*step(t1.zxy, t1.xyz); return tN > 0.001 ? tN : tF; } float iCyl(vec3 ro, vec3 rd, vec3 c, float r, float h){ vec2 oc = ro.xz - c.xz; float a = dot(rd.xz, rd.xz); if (a < 1e-6) return -1.0; float b = dot(oc, rd.xz), k = dot(oc, oc) - r*r, d = b*b - a*k; if (d < 0.0) return -1.0; float s = sqrt(d), t = (-b - s)/a, y = ro.y + rd.y*t; if (t > 0.001 && y >= c.y && y <= c.y + h) return t; t = (-b + s)/a; y = ro.y + rd.y*t; if (t > 0.001 && y >= c.y && y <= c.y + h) return t; return -1.0; } // scene state written by the intersector, read by the shader vec3 gScrC, gScrS; float gScrSeed, gScrAxis; void hSph(vec3 ro, vec3 rd, vec3 c, float r, float mid, inout float best, inout float id, inout vec3 n){ float t = iSph(ro, rd, c, r); if (t > 0.0 && t < best){ best = t; id = mid; n = normalize(ro + rd*t - c); } } void hBox(vec3 ro, vec3 rd, vec3 c, vec3 b, float mid, inout float best, inout float id, inout vec3 n){ vec3 bn; float t = iBox(ro, rd, c, b, bn); if (t > 0.001 && t < best){ best = t; id = mid; n = bn; } } void hCyl(vec3 ro, vec3 rd, vec3 c, float r, float h, float mid, inout float best, inout float id, inout vec3 n){ float t = iCyl(ro, rd, c, r, h); if (t > 0.0 && t < best){ best = t; id = mid; vec3 p = ro + rd*t; n = normalize(vec3(p.x - c.x, 0.0, p.z - c.z)); } // cap it, so a snapped column isn't a hollow pipe if (abs(rd.y) > 1e-6){ float tc = (c.y + h - ro.y)/rd.y; if (tc > 0.001 && tc < best){ vec2 d = (ro.xz + rd.xz*tc) - c.xz; if (dot(d, d) <= r*r){ best = tc; id = mid; n = vec3(0.0, 1.0, 0.0); } } } } // a REAL arch: box minus a z-axis cylinder, so the opening is carved // through solid stone and the intrados curves void hArch(vec3 ro, vec3 rd, vec3 c, vec3 b, float hy, float hr, inout float best, inout float id, inout vec3 n){ vec3 o = ro - c, m = 1.0/rd, k = abs(m)*b, t1 = -m*o - k, t2 = -m*o + k; float bN = max(max(t1.x, t1.y), t1.z), bF = min(min(t2.x, t2.y), t2.z); if (bN > bF || bF < 0.001 || bN >= best) return; vec2 oc = ro.xy - vec2(c.x, c.y + hy); float a = dot(rd.xy, rd.xy), cN = 1e9, cF = -1e9; if (a > 1e-8){ float bb = dot(oc, rd.xy), cc = dot(oc, oc) - hr*hr, hh = bb*bb - a*cc; if (hh > 0.0){ float s = sqrt(hh); cN = (-bb - s)/a; cF = (-bb + s)/a; } } else if (dot(oc, oc) < hr*hr){ cN = -1e9; cF = 1e9; } float t = bN; if (t > cN && t < cF){ // entered through the void t = cF; if (t > bF || t < 0.001 || t >= best) return; best = t; id = 6.0; n = vec3(-normalize((ro + rd*t).xy - vec2(c.x, c.y + hy)), 0.0); return; } if (t < 0.001 || t >= best) return; best = t; id = 6.0; n = -sign(rd)*step(t1.yzx, t1.xyz)*step(t1.zxy, t1.xyz); } void hTri(vec3 ro, vec3 rd, vec3 v0, vec3 v1, vec3 v2, float mid, inout float best, inout float id, inout vec3 n){ vec3 e1 = v1 - v0, e2 = v2 - v0, pv = cross(rd, e2); float det = dot(e1, pv); if (abs(det) < 1e-7) return; float inv = 1.0/det; vec3 tv = ro - v0; float u = dot(tv, pv)*inv; if (u < 0.0 || u > 1.0) return; vec3 qv = cross(tv, e1); float v = dot(rd, qv)*inv; if (v < 0.0 || u + v > 1.0) return; float t = dot(e2, qv)*inv; if (t > 0.001 && t < best){ best = t; id = mid; n = normalize(cross(e1, e2)); if (dot(n, rd) > 0.0) n = -n; } } // a beige CRT: pedestal, neck, tapered tube, bezel, screen, keyboard, mouse void hMon(vec3 ro, vec3 rd, vec3 b, float axis, float sgn, float seed, float kb, inout float best, inout float id, inout vec3 n){ vec3 f = (axis < 0.5) ? vec3(0.0, 0.0, sgn) : vec3(sgn, 0.0, 0.0); if (iSph(ro, rd, b + vec3(0.0, 0.51, 0.0) + f*0.28, 1.14) < 0.0) return; hBox(ro, rd, b + vec3(0.0, 0.05, 0.0), vec3(0.30, 0.05, 0.28), 7.0, best, id, n); // neck spans 0.10 to 0.30 so it actually MEETS the stand below it and // the tube above it — it used to start at 0.16 and float hBox(ro, rd, b + vec3(0.0, 0.20, 0.0), vec3(0.12, 0.10, 0.12), 7.0, best, id, n); vec3 cf = (axis < 0.5) ? vec3(0.44, 0.36, 0.26) : vec3(0.26, 0.36, 0.44); vec3 cr = (axis < 0.5) ? vec3(0.32, 0.27, 0.22) : vec3(0.22, 0.27, 0.32); hBox(ro, rd, b + vec3(0.0, 0.66, 0.0) + f*0.16, cf, 7.0, best, id, n); hBox(ro, rd, b + vec3(0.0, 0.62, 0.0) - f*0.28, cr, 7.0, best, id, n); float dep = 0.16 + ((axis < 0.5) ? cf.z : cf.x); hBox(ro, rd, b + vec3(0.0, 0.66, 0.0) + f*(dep + 0.02), (axis < 0.5) ? vec3(0.40, 0.32, 0.025) : vec3(0.025, 0.32, 0.40), 7.0, best, id, n); vec3 sc = b + vec3(0.0, 0.66, 0.0) + f*(dep + 0.055); vec3 ss = (axis < 0.5) ? vec3(0.33, 0.25, 0.02) : vec3(0.02, 0.25, 0.33); float b0 = best; hBox(ro, rd, sc, ss, 8.0, best, id, n); // hand the winning glass its own frame so the material can build // SCREEN-LOCAL uv (world-space uv makes distant sets render blank) if (best < b0){ gScrC = sc; gScrS = ss; gScrSeed = seed; gScrAxis = axis; } if (kb > 0.5){ // floor sets only: nothing holds these up vec3 kc = b + f*(dep + 0.50); hBox(ro, rd, kc + vec3(0.0, 0.030, 0.0), (axis < 0.5) ? vec3(0.34, 0.030, 0.145) : vec3(0.145, 0.030, 0.34), 9.0, best, id, n); hBox(ro, rd, kc - f*0.095 + vec3(0.0, 0.075, 0.0), (axis < 0.5) ? vec3(0.34, 0.022, 0.05) : vec3(0.05, 0.022, 0.34), 9.0, best, id, n); hBox(ro, rd, kc + ((axis < 0.5) ? vec3(0.48, 0.04, 0.0) : vec3(0.0, 0.04, 0.48)), vec3(0.075, 0.04, 0.10), 7.0, best, id, n); } } // Two rings, each EVENLY spaced around its own circumference: 13 at r5.2 // and 20 at r7.9. A single spiral (one angular step for both radii) packs // badly — push it past ~23 machines and pairs start overlapping. Solving // the rings separately fits 25 with every pair >2.45 apart (a unit is // ~2.2 across). The loop then skips the arcade sector, the pillar, the // marble edge and both spheres. vec3 monPos(int i){ float fi = float(i); if (fi < 13.0) return vec3(cos(fi*0.4833)*5.2, 0.0, sin(fi*0.4833)*5.2); float k = fi - 13.0; return vec3(cos(k*0.31416)*7.9, 0.0, sin(k*0.31416)*7.9); } // ---- the scene ---------------------------------------------- // A sphere's centre height must equal its radius to sit on the floor. // Enlarging both meant moving the glass out: at r 1.75 + 1.30 the old // 2.75-unit gap would have had them interpenetrating. #define CHROME vec3(0.0, 1.75, 0.0) #define CH_R 1.75 #define GLASS vec3(-3.2, 1.30, 1.6) #define GBALL_R 1.30 float hitScene(vec3 ro, vec3 rd, out float id, out vec3 n){ float best = 1e9; id = -1.0; n = vec3(0.0, 1.0, 0.0); if (rd.y < -0.0001){ float t = -ro.y/rd.y; if (t > 0.001){ best = t; id = 0.0; n = vec3(0.0, 1.0, 0.0); } } hSph(ro, rd, CHROME, CH_R, 1.0, best, id, n); hSph(ro, rd, GLASS, GBALL_R, 2.0, best, id, n); // terminals vec3 mg; if (iBox(ro, rd, vec3(0.0, 0.6, 0.0), vec3(8.6, 0.75, 8.6), mg) > 0.0){ for (int i = 0; i < 33; i++){ vec3 mp = monPos(i); if (mp.z < -5.2) continue; // arcade sector if (distance(mp.xz, vec2(6.6, 1.4)) < 1.8) continue; // the pillar if (max(abs(mp.x), abs(mp.z)) > 7.9) continue; // keep off the sand if (length(mp.xz) < CH_R + 1.30) continue; // the chrome ball if (distance(mp.xz, GLASS.xz) < GBALL_R + 1.30) continue; // the glass ball float ax = (abs(mp.x) > abs(mp.z)) ? 1.0 : 0.0; // face outward hMon(ro, rd, mp, ax, (ax > 0.5) ? sign(mp.x) : sign(mp.z), float(i), 1.0, best, id, n); } } // a pillar with one more terminal on top, showing the scene itself // Built to the same order as the arcade columns rather than as a bare // cylinder: square plinth, round torus base, a shaft of four drums // that taper as they rise (entasis, and fluted — material 5), then // echinus and abacus. The abacus top lands at 2.40, which is where // the terminal already sat, so the machine doesn't move. hBox(ro, rd, vec3(6.6, 0.09, 1.4), vec3(0.46, 0.09, 0.46), 6.0, best, id, n); // plinth hCyl(ro, rd, vec3(6.6, 0.18, 1.4), 0.40, 0.10, 6.0, best, id, n); // torus base for (int d = 0; d < 4; d++){ float fd = float(d); hCyl(ro, rd, vec3(6.6, 0.28 + 0.45*fd, 1.4), 0.34 - 0.04*fd/3.0, 0.45, 5.0, best, id, n); // fluted drums } hCyl(ro, rd, vec3(6.6, 2.08, 1.4), 0.40, 0.14, 6.0, best, id, n); // echinus hBox(ro, rd, vec3(6.6, 2.31, 1.4), vec3(0.48, 0.09, 0.48), 6.0, best, id, n); // abacus hMon(ro, rd, vec3(6.6, 2.40, 1.4), 1.0, -1.0, 99.0, 0.0, best, id, n); // the arcade: stylobate, 4 drum-built columns, 3 masonry arches vec3 ag; if (iBox(ro, rd, vec3(-3.2, 2.3, -6.9), vec3(4.4, 2.4, 1.2), ag) > 0.0){ hBox(ro, rd, vec3(-3.2, 0.18, -6.9), vec3(4.0, 0.18, 0.95), 6.0, best, id, n); hBox(ro, rd, vec3(-3.2, 0.50, -6.9), vec3(3.8, 0.16, 0.80), 6.0, best, id, n); for (int i = 0; i < 4; i++){ float x = -6.4 + float(i)*1.6; float sh = (i == 3) ? 1.6 : 2.7; // one snapped short hBox(ro, rd, vec3(x, 0.73, -6.9), vec3(0.42, 0.07, 0.42), 6.0, best, id, n); for (int d = 0; d < 4; d++){ float fd = float(d); hCyl(ro, rd, vec3(x, 0.80 + sh/4.0*fd, -6.9), 0.30 - 0.03*fd/3.0, sh/4.0, 5.0, best, id, n); } if (i != 3){ hCyl(ro, rd, vec3(x, 0.80 + sh, -6.9), 0.34, 0.12, 6.0, best, id, n); hBox(ro, rd, vec3(x, 3.68, -6.9), vec3(0.40, 0.08, 0.40), 6.0, best, id, n); } } for (int i = 0; i < 3; i++) hArch(ro, rd, vec3(-5.6 + float(i)*1.6, 3.90, -6.9), vec3(0.80, 0.40, 0.26), -0.35, 0.54, best, id, n); hBox(ro, rd, vec3(-4.0, 4.46, -6.9), vec3(2.75, 0.16, 0.46), 6.0, best, id, n); } // eyeballs — continuous drift only, and high enough to clear the arcade vec3 eg; if (iBox(ro, rd, vec3(0.0, 7.4, 0.0), vec3(11.5, 3.3, 11.5), eg) > 0.0){ for (int i = 0; i < 10; i++){ if (float(i) >= uEyes) break; float fe = float(i); hSph(ro, rd, eyePos(fe, 0.0), eyeRad(fe, 0.0), 10.0, best, id, n); } // a tighter swarm of little ones circling just above the chrome for (int i = 0; i < 8; i++){ if (float(i) >= uEyes*0.8) break; float fe = float(i); hSph(ro, rd, eyePos(fe, 1.0), eyeRad(fe, 1.0), 10.0, best, id, n); } } // beach balls, bobbing where the marble meets the sand for (int i = 0; i < 5; i++){ float fb2 = float(i); float ba = fb2*1.257 + 0.6; // >=10.3 keeps them clear of the outer terminal ring (which // reaches 9.05) and puts them out on the sand / in the shallows float br = 10.3 + 0.9*h21(vec2(fb2, 3.3)); float rad = 0.42 + 0.20*h21(vec2(fb2, 8.1)); hSph(ro, rd, vec3(cos(ba)*br, rad + 0.10*abs(sin(gT*1.1 + fb2*2.0)), sin(ba)*br), rad, 12.0, best, id, n); } // shark fins. The shore is a CHEBYSHEV boundary but these swim a // EUCLIDEAN circle, so the radius must clear 13.35*sqrt(2) = 18.9 or // they surface on the beach. for (int i = 0; i < 6; i++){ float fs = float(i); float an = gT*(0.050 + 0.018*fs) + fs*1.9; float rr = 20.0 + 2.8*fs + 1.2*sin(gT*0.3 + fs); vec3 fb = vec3(cos(an)*rr, -0.08, sin(an)*rr); vec3 dr = vec3(-sin(an), 0.0, cos(an)); vec3 tp = fb - dr*0.55 + vec3(0.0, 1.40, 0.0); hTri(ro, rd, fb + dr*0.80, tp, fb - dr*0.95, 11.0, best, id, n); hTri(ro, rd, fb - dr*0.95, tp, fb - dr*0.40 + vec3(0.0, 0.55, 0.0), 11.0, best, id, n); } return best; } float shadowRay(vec3 p){ vec3 dn; if (iSph(p, LD, CHROME, CH_R) > 0.0) return 0.0; if (iBox(p, LD, vec3(0.0, 0.6, 0.0), vec3(8.6, 0.75, 8.6), dn) > 0.001){ for (int i = 0; i < 33; i++){ vec3 b = monPos(i); if (iBox(p, LD, b + vec3(0.0, 0.55, 0.0), vec3(0.45, 0.55, 0.45), dn) > 0.001) return 0.0; } } if (iBox(p, LD, vec3(-4.0, 4.06, -6.9), vec3(2.75, 0.56, 0.50), dn) > 0.001) return 0.0; if (iCyl(p, LD, vec3(6.6, 0.0, 1.4), 0.42, 2.40) > 0.001) return 0.0; float s = 1.0; if (iSph(p, LD, GLASS, GBALL_R) > 0.0) s *= 0.45; // glass only tints return s; } // ---- the tracer --------------------------------------------- vec3 trace(vec3 ro, vec3 rd){ vec3 col = vec3(0.0), att = vec3(1.0); for (int i = 0; i < 4; i++){ float id; vec3 n; float t = hitScene(ro, rd, id, n); if (id < 0.0){ vec3 sky = skyCol(rd); if (uSkyArt > 0.001 && rd.y > 0.02){ // reuse a terminal's program as a sky panel. Seeds stay // under 90 so this can't reach sceneFeed -> skyCol, which // would be recursion (illegal in GLSL). float band = floor(atan(rd.x, rd.z)/TAU*4.0 + 4.0); vec2 suv = vec2(fract(atan(rd.x, rd.z)/TAU*4.0), clamp(rd.y*1.7, 0.0, 1.0)); sky = mix(sky, screenArt(suv, band*3.0 + 1.0), uSkyArt*smoothstep(0.02, 0.35, rd.y)*0.75); } col += att*sky; break; } vec3 p = ro + rd*t, rr = reflect(rd, n); float sh = (i < 1) ? shadowRay(p + n*0.002) : 1.0; float dif = max(dot(n, LD), 0.0)*sh; float spec = pow(max(dot(rr, LD), 0.0), 90.0)*sh; if (id < 2.5 && id > 1.5){ // glass: fresnel reflection, then refract through and carry on float c = clamp(-dot(rd, n), 0.0, 1.0); float fr = 0.04 + 0.96*pow(1.0 - c, 5.0); col += att*(skyCol(rr)*fr*0.85 + vec3(1.0)*spec*1.6); vec3 ri = refract(rd, n, 0.667); vec3 oc = p - GLASS; float b = dot(oc, ri); float t2 = -b + sqrt(max(b*b - dot(oc, oc) + GBALL_R*GBALL_R, 0.0)); vec3 p2 = p + ri*t2; vec3 ro2 = refract(ri, -normalize(p2 - GLASS), 1.5); if (dot(ro2, ro2) < 0.5) ro2 = ri; att *= exp(-t2*vec3(0.50, 0.05, 0.22))*(1.0 - fr*0.7); ro = p2 + ro2*0.002; rd = ro2; } else { vec3 alb, tint = vec3(1.0); float kr, ks = 1.0, gs = uGloss; vec3 emis = vec3(0.0); if (id < 0.5){ float e = max(abs(p.x), abs(p.z)); float ea = atan(p.z, p.x); float esh = 11.3 + 1.1*sin(ea*3.0 + 0.5) + 0.6*sin(ea*6.0 + 2.0) + 0.35*sin(ea*11.0); if (e > esh){ n = normalize(vec3(uWave*(0.030*sin(p.x*1.4 + gT*1.2) + 0.018*sin((p.x + p.z)*2.7 - gT*1.7)), 1.0, uWave*(0.026*sin(p.z*1.1 - gT*0.9) + 0.014*sin((p.z - p.x)*3.1 + gT*1.4)))); rr = reflect(rd, n); dif = max(dot(n, LD), 0.0)*sh; spec = pow(max(dot(rr, LD), 0.0), 200.0)*sh; alb = hueShift(vec3(0.01, 0.07, 0.12), uWaveHue); kr = (0.10 + 0.85*pow(1.0 - clamp(-dot(rd, n), 0.0, 1.0), 3.0))*(0.25 + 0.75*uGloss); tint = hueShift(vec3(0.75, 0.90, 1.0), uWaveHue); ks = 3.0; gs = 1.0; } else if (e > PLAZA){ vec3 sc = vec3(0.75, 0.67, 0.48); sc *= 1.0 + vnoise(p.xz*7.0)*0.13 + vnoise(p.xz*24.0)*0.06 - 0.09 + 0.05*sin(e*7.0 + vnoise(p.xz*2.0)*4.0); float wet = smoothstep(esh - 1.6, esh - 0.1, e); sc = mix(sc, vec3(0.40, 0.38, 0.32), wet*0.75); sc = mix(sc, vec3(0.96, 0.98, 1.0), clamp(smoothstep(esh - 0.35, esh - 0.08, e) * smoothstep(esh + 0.05, esh - 0.08, e), 0.0, 1.0)*0.85); alb = sc; kr = 0.02 + wet*0.10; ks = 0.15 + wet*0.6; tint = vec3(0.8, 0.9, 1.0); } else { // polished veined marble, cracked float vv = smoothstep(0.34, 0.66, fbm(p.xz*1.7 + vec2(fbm(p.xz*0.6)*2.4))); vec3 cA = mix(vec3(0.20, 0.11, 0.11), vec3(0.10, 0.05, 0.06), vv); vec3 cB = mix(vec3(0.93, 0.89, 0.82), vec3(0.74, 0.68, 0.60), vv); alb = mix(cA, cB, mod(floor(p.x) + floor(p.z), 2.0)); float far = clamp(t*0.010 - 0.12, 0.0, 1.0); alb = mix(alb, 0.5*(cA + cB), far); kr = 0.34; ks = 1.7; tint = vec3(0.98, 0.96, 0.94); float k1 = 1.0 - smoothstep(0.0, 0.013, abs(fbm(p.xz*0.85 + 11.0) - 0.5)); float k2 = 1.0 - smoothstep(0.0, 0.009, abs(fbm(p.xz*2.30 - 4.0) - 0.5)); float ck = clamp(max(k1, k2*0.7)*(1.0 - far), 0.0, 1.0); alb = mix(alb, vec3(0.07, 0.05, 0.05), ck*0.72); kr *= 1.0 - ck*0.92; ks *= 1.0 - ck*0.8; } } else if (id < 1.5){ alb = vec3(0.02); kr = 0.88; tint = vec3(0.95, 0.97, 1.0); ks = 1.8; } else if (id < 5.5){ // fluted column shaft float a2 = atan(n.z, n.x); float fl = mix(1.0, 0.70 + 0.30*abs(sin(a2*10.0)), clamp(1.0 - abs(n.y)*1.6, 0.0, 1.0)); alb = vec3(0.92, 0.90, 0.86)*fl*(0.88 + 0.12*vnoise(vec2(a2*2.5, p.y*3.0))); kr = 0.10; ks = 1.1; } else if (id < 6.5){ alb = vec3(0.93, 0.91, 0.87)*(0.88 + vnoise(p.xz*2.2 + p.y*1.8)*0.16); kr = 0.10; ks = 1.1; } else if (id < 7.5){ // yellowed beige plastic with moulded vents alb = mix(vec3(0.80, 0.76, 0.63), vec3(0.66, 0.59, 0.44), vnoise(p.xz*3.5 + p.y*2.5)); alb *= 0.86 + 0.14*step(0.35, fract(p.y*30.0)); kr = 0.04; ks = 0.65; } else if (id < 8.5){ // the glass: screen-local uv, then whatever it's running vec3 dl = (p - gScrC)/max(gScrS, vec3(1e-4)); vec2 uv = clamp(((gScrAxis < 0.5) ? dl.xy : dl.zy)*0.5 + 0.5, 0.0, 1.0); vec3 art = screenArt(uv, gScrSeed); vec2 cq = uv*2.0 - 1.0; alb = art*0.04; emis = art*(0.78 + 0.22*sin(uv.y*190.0)) * (0.94 + 0.06*sin(gT*13.0 + gScrSeed*31.0)) * smoothstep(2.1, 0.6, dot(cq, cq))*uScreens; kr = 0.10; ks = 1.4; tint = vec3(0.80, 0.86, 1.0); } else if (id < 9.5){ // keyboard: per-key grid on the top face only if (n.y > 0.5){ vec2 kk = fract(vec2(p.x*11.0, p.z*11.0)) - 0.5; alb = mix(vec3(0.42, 0.40, 0.36), vec3(0.78, 0.75, 0.66)*(0.92 + 0.08*h21(floor(vec2(p.x, p.z)*11.0))), step(max(abs(kk.x), abs(kk.y)), 0.36)); } else alb = vec3(0.72, 0.68, 0.57); kr = 0.03; ks = 0.5; } else if (id < 12.5 && id > 11.5){ // beach ball: six vertical wedges in primary colours float wa = atan(n.z, n.x)/TAU + 0.5; float wi = floor(fract(wa)*6.0); vec3 wc = (wi < 1.0) ? vec3(0.92, 0.18, 0.16) : (wi < 2.0) ? vec3(0.97, 0.94, 0.90) : (wi < 3.0) ? vec3(0.14, 0.40, 0.85) : (wi < 4.0) ? vec3(0.97, 0.94, 0.90) : (wi < 5.0) ? vec3(0.98, 0.78, 0.12) : vec3(0.97, 0.94, 0.90); alb = mix(wc, vec3(0.97, 0.95, 0.92), smoothstep(0.72, 0.97, abs(n.y))); kr = 0.10; ks = 1.5; } else if (id < 10.5){ // eyeball, pupil aimed at the courtyard float d = dot(n, normalize(vec3(0.0, 1.0, 0.0) - p + vec3(1e-3))); alb = mix(vec3(0.95, 0.93, 0.91), vec3(0.72, 0.10, 0.08), smoothstep(0.52, 0.92, fbm(n.xy*8.0 + n.z*4.0))*0.55); alb = mix(alb, vec3(0.16, 0.46, 0.62), smoothstep(0.70, 0.85, d)); alb = mix(alb, vec3(0.02, 0.02, 0.03), smoothstep(0.90, 0.955, d)); kr = 0.12; ks = 2.6; } else { alb = vec3(0.15, 0.18, 0.23)*(0.80 + 0.35*max(n.y, 0.0)); // shark kr = 0.12; ks = 1.2; tint = vec3(0.8, 0.9, 1.0); } kr *= gs; vec3 SUN = hueShift(vec3(1.05, 0.74, 0.42), uSkyHue); vec3 AMB = hueShift(vec3(0.16, 0.20, 0.34), uSkyHue); col += att*(alb*(AMB*0.55 + SUN*1.15*dif)*(1.0 - kr) + SUN*spec*ks + emis); att *= kr*tint; ro = p + n*0.002; rd = rr; } if (max(att.x, max(att.y, att.z)) < 0.02) break; } return col; } void main(){ vec2 R = RENDERSIZE.xy; vec2 pc = gl_FragCoord.xy; gT = TIME + 11.0; LD = normalize(vec3(-0.62, 0.27, -0.44)); float ang = 0.55 + gT*0.22*uSpeed; vec2 uv = (2.0*pc - R)/R.y; vec3 cp = vec3(sin(ang)*11.9, uCamHt + 0.45*sin(ang*0.6), cos(ang)*11.9); vec3 fw = normalize(vec3(0.0, 1.0, 0.0) - cp); vec3 rt = normalize(cross(fw, vec3(0.0, 1.0, 0.0))); vec3 col = trace(cp, normalize(fw*uFOV + rt*uv.x + cross(rt, fw)*uv.y)); col = pow(clamp(col, 0.0, 1.0), vec3(0.4545)); float L = max(2.0, floor(uPalette + 0.5)); col = floor(col*(L - 1.0) + 0.5 + (bayer(pc) - 0.5)*uDither)/(L - 1.0); col *= 1.0 - uCRT*0.4*(0.5 + 0.5*sin(pc.y*3.14159)); vec2 q = pc/R; col *= mix(1.0, pow(16.0*q.x*q.y*(1.0 - q.x)*(1.0 - q.y), 0.35), uVignette); gl_FragColor = vec4(col, 1.0); }
A mid-1990s raytracer showpiece at sunset, traced for real. A chrome sphere and a refracting glass sphere preside over a grid of dead beige CRT terminals whose phosphor screens run little demoscene programs - source code typed out character by character, plasma, a checkered tunnel, digital rain, and one showing a live view of this very scene - all mirrored in a cracked, polished veined-marble checkerboard. Beside them stands a ruined Greek arcade: drum-built columns carrying real masonry arches, each a semicircular void cut through a stone spandrel by CSG. Shark fins cut the glittering lake, eyeballs drift overhead, and an Earth hangs in a sky that runs from a molten orange horizon through violet to a black star field. Whitted-style recursive reflections and refraction, one low warm key light with long hard shadows and cool skylight fill. Sliders: orbit, lens, camera height, eyeballs, eye speed, screen glow, sky hue, water hue, waves, reflection, palette depth, dither, scanlines, vignette, sky-art (paints the terminals' programs across the sky).
export clip · render a video in your browser
Renders to video in your browser (no upload), stepping the real renderer frame by frame so multipass + feedback come out right. motion auto-animates the shader's own knobs — each export rolls a fresh take (🎲 re-roll, 🔒 to pin one you like). GIF is slower with a tighter length cap; MP4/WebM are faster. Some devices record in real time (noted below if so) — keep the tab visible.
embed · drop this shader into any page
Renders client-side in the reader's browser (CC0 — no attribution required, but the backlink is appreciated).