Ray marching with sculpting, twisting, displacement, repetition, and sort of a fog.
Log in to post a comment.
// Forked from "Making waves" by PavlikEnemy // https://turtletoy.net/turtle/13016b43d5 Canvas.setpenopacity(-0.3); const t = new Turtle(); const scrZ = 6.0; const scrR = 1.0; const eyeZ = 9.0; const camRotX = -60 * Math.PI/180; const camRotY = -15 * 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 plane_df(p) { // z = 0 return p[2]; } function sphere_df(p, c, r) { return length(minus(c, p)) - r; } // 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 smooth_max(a, b, k) { return -smooth_min(-a, -b, k); } function scene_df(p) { const r = 0.075; const h = 2.5; p0 = p; const periods = [1.0, 1.5, 100.0]; const half_periods = multiply(0.5, periods); let hx = Math.floor((Math.abs(p[0]) + periods[0]/2) / periods[0]); let hy = Math.floor((Math.abs(p[1]) + periods[1]/2) / periods[1]); let hash = 1 - 2 * ((Math.sin(hx * 12.9898 + hy * 78.233) * 43758.5453) % 1); //let phi = p[2] * 5 - Math.PI/2; let phi = p[2] * 5 - 2 * Math.PI * hash; p = plus(p, [101, 99, 0]); p = minus(mod(plus(p, half_periods), periods), half_periods); let [px, py] = rotate([p[0], p[1]], phi); let p_tr = [px, py, p[2]]; let c = [0, 0.1, 0]; let cyl_df = cylinder_df(p_tr, c, h, r); let cr1 = 2 * r; let cs1 = [c[0], c[1], c[2] + h/2]; let sph1_df = sphere_df(p_tr, cs1, cr1); let cr3 = 1.25 * r; let a = 1.25; let dr = 1.5 * cr1; let cs3 = [c[0] - dr, c[1], c[2] + h/2 + a * cr1]; let sph3_df = sphere_df(p_tr, cs3, cr3); cs3 = [c[0] + dr, c[1], c[2] + h/2 + a * cr1]; sph3_df = Math.min(sph3_df, sphere_df(p_tr, cs3, cr3)); cs3 = [c[0], c[1] - dr, c[2] + h/2 + a * cr1]; sph3_df = Math.min(sph3_df, sphere_df(p_tr, cs3, cr3)); cs3 = [c[0], c[1] + dr, c[2] + h/2 + a * cr1]; sph3_df = Math.min(sph3_df, sphere_df(p_tr, cs3, cr3)); let cr2 = 2.0 * r; let cs2 = [c[0], c[1], c[2] + h/2 + cr1]; let sph2_df = sphere_df(p_tr, cs2, cr2); let pl_df = plane_df(p); let noise1 = 0.015 * Math.sin(30 * p_tr[0]) * Math.sin(30 * p_tr[1]) * Math.sin(40 * p_tr[2]); //let noise2 = 0.025 * Math.sin(31 * (p0[0] + 2 * p0[1])) * Math.sin(29 * (p0[1])) * Math.sin(40 * p0[2]); var df; df = smooth_min(cyl_df, sph1_df, 0.05); df = smooth_min(df, sph3_df, 0.3); df = smooth_max(df, -sph2_df, 0.05); df += noise1 //pl_df += noise2 //pl_df += 0.005 * (1 - 2 * ((Math.sin(10*p0[0] * 12.9898 + 10*p0[1] * 78.233) * 43758.5453) % 1)) pl_df += 0.2 * (1 - 2 * ((Math.sin(10*p0[0] * 12.9898 + 10*p0[1] * 78.233) * 43758.5453) % 1)) df = smooth_min(pl_df, df, 0.75); //df += 0.005 * (1 - 2 * ((Math.sin(10*p0[0] * 12.9898 + 10*p0[1] * 78.233) * 43758.5453) % 1)) //df += 0.002 * (1 - 2 * ((Math.sin(10*p0[0] * 12.9898 + 10*p0[1] * 78.233) * 43758.5453) % 1)) 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))**2.5; 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); }