My first ever attempt at ray marching (from scratch)
Log in to post a comment.
Canvas.setpenopacity(-0.3); const t = new Turtle(); const scrZ = 4.0; const scrR = 1.0; const eyeZ = 8.0; const camRotX = -60 * Math.PI/180; const step = 0.25; const R = 1.0; const R1 = 0.5; const R2 = 0.15; const Dmax = scrZ + 2 * scrR; const INF = 2 * Dmax; const EPS = 0.0001; const MAX_N_STEPS = 256; const glowR = 0.2 * R; function plus(v1, v2) { return [v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]]; } function minus(v1, v2) { return [v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]]; } function multiply(a, v) { return [a * v[0], a * v[1], a * v[2]]; } function length(v) { return Math.sqrt(v[0]**2 + v[1]**2 + v[2]**2); } function normalize(v) { let L = length(v); return [v[0] / L, v[1] / L, v[2] / L]; } function torus_df(p, c, R, r) { // axis z let pc = minus(p, c); let px = pc[0], py = pc[1], pz = pc[2]; let dxy = Math.sqrt(px**2 + py**2); let cx = px / dxy * R; let cy = py / dxy * R; let d = [px - cx, py - cy, pz]; return length(d) - r; } function cylinder_df(p, h, r) { // center (0, 0, 0), axis z let px = p[0], py = p[1], pz = p[2]; let dxy = Math.sqrt(px**2 + py**2) - r; let dz = Math.abs(pz) - h/2; if (dxy > 0) { if (dz > 0) { return Math.sqrt(dxy**2 + dz**2); } else { return dxy; } } else { if (dz > 0) { return dz; } else { return Math.max(dxy, dz); } } } // see https://iquilezles.org/articles/smin/ function smooth_min(a, b, k) { let h = Math.max(k - Math.abs(a - b), 0) / k; return Math.min(a, b) - h*h*h*k*(1/6); } function scene_df(p) { let phi = -Math.PI / 12; let phi1 = phi - Math.PI / 6, phi2 = phi - 5 * Math.PI / 6, phi3 = phi - 3 * Math.PI / 2; let df1 = torus_df(p, [R * Math.cos(phi1), R * Math.sin(phi1), 0], R1, R2); let df2 = torus_df(p, [R * Math.cos(phi2), R * Math.sin(phi2), 0], R1, R2); let df3 = torus_df(p, [R * Math.cos(phi3), R * Math.sin(phi3), 0], R1, R2); let df4 = cylinder_df(p, 4 * R2, 0.7 * (R - R1 - R2)); let df12 = smooth_min(df1, df2, 0.35); let df123 = smooth_min(df12, df3, 0.35); let df1234 = smooth_min(df123, df4, 0.75); return df1234; } let x = 0; let y = 0; function walk(frame) { let px = -scrR + 2 * scrR * (x / 200); let py = -scrR + 2 * scrR * (y / 200); let pz = scrZ; [py, pz] = [py * Math.cos(camRotX) - pz * Math.sin(camRotX), py * Math.sin(camRotX) + pz * Math.cos(camRotX)]; let ex = 0, ey = 0, ez = eyeZ; [ey, ez] = [ey * Math.cos(camRotX) - ez * Math.sin(camRotX), ey * Math.sin(camRotX) + ez * Math.cos(camRotX)]; let p = [px, py, pz]; let dir = normalize([px - ex, py - ey, pz - ez]); let D = 0; let d_min = INF; var n_steps; for (n_steps = 0; n_steps < MAX_N_STEPS; n_steps++) { let d = scene_df(p); d_min = Math.min(d_min, d); if ((d < EPS) || (D > Dmax)) { break; } else { p = plus(p, multiply(d, dir)); D += d; } } if (D < Dmax) { let s = step * Math.max(0.1, (1 - n_steps / 50)); t.seth(360 * Math.random()); for (let i = 0; i < 4; i++) { t.jump(x - 100, y - 100); t.right(90); t.forward(s); } } if ((d_min > EPS) && (d_min < glowR)) { let s = step * (1 - d_min / glowR)**2; t.seth(360 * Math.random()); for (let i = 0; i < 4; i++) { t.jump(x - 100, y - 100); t.right(90); t.forward(s); } } x += step; if (x > 200) { y += step; x = 0; } return (y < 200); }