I have combined the raytraced scene of Raytraced sphere #1 with the circle-packing algorithm of Circle packing #1 to get some sort of dithering.
#raytracer #pixels #rays #dithering
Log in to post a comment.
// Raytraced Sphere #3. Created by Reinder Nijhoff 2018 // @reindernijhoff // // https://turtletoy.net/turtle/7367ba3a0c // Canvas.setpenopacity(1); const canvas_size = 95; const light_position = [-2,3,-4]; const ro = [0,0,-3.5]; const sphere_pos = [-.2,0,0]; const max_radius = 2; const min_radius = .55; const radius_decr = .9; const max_tries = 400; const circle_radius = .5; const circle_buckets = []; const circle_num_buckets = canvas_size/max_radius; let radius = max_radius; const circles = []; const turtle = new Turtle(); for (let i=0; i<circle_num_buckets+2; i++) { circle_buckets[i]=[]; for (let j=0; j<circle_num_buckets+2; j++) { circle_buckets[i][j] = []; } } function add_circle(t, r) { let coord_found = false; let tries = 0; const drdr = r*r*2; while (!coord_found && tries < max_tries) { tries ++; const x = Math.random() * (canvas_size-r)*2 -canvas_size + r; const y = Math.random() * (canvas_size-r)*2 -canvas_size + r; let possible = true; const xb = Math.max(0,((.5*x/canvas_size+.5)*circle_num_buckets)|0); const yb = Math.max(0,((.5*y/canvas_size+.5)*circle_num_buckets)|0); for (let xbi = Math.max(0,xb-1); xbi<xb+2 && possible; xbi++) { for (let ybi = Math.max(0,yb-1); ybi<yb+2 && possible; ybi++) { const circles = circle_buckets[xbi][ybi]; for (let i=0; i<circles.length && possible; i++) { const dx = circles[i][0] - x; const dy = circles[i][1] - y; if ( dx*dx + dy*dy < drdr) { possible = false; break; } } } } if (possible) { coord_found = true; draw_circle(x,y,t, r); circle_buckets[xb][yb].push([x,y]); return true; } } return false; } function draw_circle(x,y,t,r) { const intensity = get_image_intensity(x/canvas_size, y/canvas_size); // use intensity squared because it looks better if ((r-min_radius)/max_radius > .65 * intensity * intensity) { turtle.penup(); turtle.goto(x,y-circle_radius); turtle.pendown(); turtle.circle(circle_radius); } } function walk(i) { if (!add_circle(turtle, radius)) { radius *= radius_decr; } return radius >= min_radius; } function get_image_intensity(x,y) { const rd = normalize3([x,-y,2]); let normal; let light = 0; let hit; let plane_hit = false; let dist = intersect_sphere(ro, rd, sphere_pos, 1); if (dist > 0) { hit = add3(ro, scale3(rd, dist)); normal = normalize3(hit); } else { dist = 10000; } if (rd[1] < 0) { const plane_dist = -1/rd[1]; if (plane_dist < dist) { dist = plane_dist; plane_hit = true; hit = add3(ro, scale3(rd, dist)); normal = [0,1,0]; } } if (dist > 0 && dist < 100) { let vec_to_light = sub3(hit, light_position); const light_dist_sqr = dot3(vec_to_light, vec_to_light); vec_to_light = scale3(vec_to_light, -1/Math.sqrt(light_dist_sqr)); let light = dot3(normal, vec_to_light); light *= 30 / light_dist_sqr; // shadow ? if (plane_hit && intersect_sphere(hit, vec_to_light, sphere_pos, 1) > 0) { light = 0; } return Math.sqrt(Math.min(1, Math.max(0,light))); } else { return 0; } } const scale3=(a,b)=>[a[0]*b,a[1]*b,a[2]*b]; const len3=(a)=>Math.sqrt(dot3(a,a)); const normalize3=(a)=>scale3(a,1/len3(a)); const add3=(a,b)=>[a[0]+b[0],a[1]+b[1],a[2]+b[2]]; const sub3=(a,b)=>[a[0]-b[0],a[1]-b[1],a[2]-b[2]]; const dot3=(a,b)=>a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; function intersect_sphere(ro, rd, center, radius) { const oc = sub3(ro, center); const b = dot3( oc, rd ); const c = dot3( oc, oc ) - radius * radius; const h = b*b - c; if( h<0 ) return -1; return -b - Math.sqrt( h ); }