Ray marching with warped infinite repetition
Log in to post a comment.
// reusing some code from my first ray marching // https://turtletoy.net/turtle/dbb1b8a695 Canvas.setpenopacity(-0.3); const t = new Turtle(); const scrZ = 4.0; const scrR = 1.0; const eyeZ = 8.0; const camRotX = -65 * Math.PI/180; const camRotY = 45 * Math.PI/180; const step = 0.25; const Dmax = scrZ + 120; const EPS = 0.0001; const MAX_N_STEPS = 256; 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 mod(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 abs(v) { return [Math.abs(v[0]), Math.abs(v[1]), Math.abs(v[2])]; } function length(v) { return Math.sqrt(v[0]**2 + v[1]**2 + v[2]**2); } function normalize(v) { return multiply(1 / length(v), v); } function rotate(v, phi) { return [v[0] * Math.cos(phi) - v[1] * Math.sin(phi), v[0] * Math.sin(phi) + v[1] * Math.cos(phi)]; } function cast_ray_from_screen(x, y) { let px = -scrR + 2 * scrR * (x / 200); let py = -scrR + 2 * scrR * (y / 200); let pz = scrZ; [px, py] = rotate([px, py], camRotY); [py, pz] = rotate([py, pz], camRotX); let ex = 0, ey = 0, ez = eyeZ; //[ex, ey] = rotate([ex, ey], camRotY) [ey, ez] = rotate([ey, ez], camRotX) let p = [px, py, pz]; let eye = [ex, ey, ez]; let dir = normalize(minus(p, eye)); return [p, dir]; } function intersect_df(p, dir, df) { let D = 0; var n_steps; for (n_steps = 0; n_steps < MAX_N_STEPS; n_steps++) { let d = scene_df(p); if ((d < EPS) || (D > Dmax)) { break; } else { p = plus(p, multiply(d, dir)); D += d; } } return [p, D, n_steps]; } function cylinder_df(p, c, h, r) { // axis z const [px, py, pz] = minus(p, c); 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); } } } function scene_df(p) { const r = 0.075; const h = 2.5; const periods = [3.1 * r, 3.1 * r, 100.0]; const half_periods = multiply(0.5, periods); let d = 0.75 * r; let phi = 2 * Math.PI * Math.floor((Math.abs(p[1]) + periods[1]/2) / periods[1]) / 10; let c = [d * Math.cos(phi), 0, d * Math.sin(phi)]; // based on infinite repeat from https://iquilezles.org/articles/distfunctions/ p = abs(p); p = minus(mod(plus(p, half_periods), periods), half_periods); let df = cylinder_df(p, c, h, r); return df; } let x = 0; let y = 0; function walk(frame) { const [p, dir] = cast_ray_from_screen(x, y); const [p_int, dist_to_int, n_steps] = intersect_df(p, dir, scene_df); if (dist_to_int < 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); } } x += step; if (x > 200) { y += step; x = 0; } return (y < 200); }