Log in to post a comment.
// Forked from "Floyd–Steinberg dithering" by imakerobots // raytraced sphere part from https://turtletoy.net/turtle/11075dfee0 const canvas_size = 95; const brown_rot = 360; const brown_for_min = 1; const brown_for_max = 10; const light_position = [-2,3,-4]; const ro = [0,0,-3.5]; const sphere_pos = [-.2,0,0]; function get_image_intensity(x,y) { x /= canvas_size; y /= canvas_size; const rd = vec_normalize([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 = vec_add(ro, vec_mul(rd, dist)); normal = vec_normalize(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 = vec_add(ro, vec_mul(rd, dist)); normal = [0,1,0]; } } if (dist > 0 && dist < 100) { let vec_to_light = vec_sub(hit, light_position); const light_dist_sqr = vec_dot(vec_to_light, vec_to_light); vec_to_light = vec_mul(vec_to_light, -1/Math.sqrt(light_dist_sqr)); let light = vec_dot(normal, vec_to_light); light *= 20 / light_dist_sqr; // changed the intensity a bit // shadow ? if (plane_hit && intersect_sphere(hit, vec_to_light, sphere_pos, 1) > 0) { light = 0.01; // added some ambient light. make 0 for none. } return Math.sqrt(Math.min(1, Math.max(0,light))); } else { return 0; } } // math functions function vec_normalize(a) { const l = Math.sqrt(vec_dot(a,a)); return [a[0]/l,a[1]/l,a[2]/l]; } function vec_add(a, b) { return [a[0]+b[0], a[1]+b[1], a[2]+b[2]] } function vec_mul(a, b) { return [a[0]*b, a[1]*b, a[2]*b] } function vec_sub(a, b) { return [a[0]-b[0], a[1]-b[1], a[2]-b[2]] } function vec_dot(a, b) { return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; } function intersect_sphere(ro, rd, center, radius) { const oc = vec_sub(ro, center); const b = vec_dot( oc, rd ); const c = vec_dot( oc, oc ) - radius * radius; const h = b*b - c; if( h<0 ) return -1; return -b - Math.sqrt( h ); } // dithering part from https://turtletoy.net/turtle/d47e2bad0c Canvas.setpenopacity(1); // Global code will be evaluated once. const turtle = new Turtle(); const level_of_detail=2; // min=1.5, max=20, step=0.5 turtle.penup(); turtle.goto(-100,-100); const stepSize = level_of_detail/10.0; const stepsPerLine = Math.floor(200.0/stepSize); stepsRemaining=stepsPerLine*stepsPerLine; dx=1; error1 = new Array(stepsPerLine); error2 = new Array(stepsPerLine); for(i=0;i<error1.length;++i) { error1[i] = error2[i] = 0; } function newLine() { for(var i=0;i<stepsPerLine;++i) { error1[i] = error2[i]; error2[i] = 0; } } function get_pixel(x,y) { return get_image_intensity( x,y ); } function find_closest_palette_color(oldpixel) { return oldpixel>0.5?1.0:0.0; } function walk(i) { var x=turtle.x()/stepSize+stepsPerLine/2; var y=turtle.y()/stepSize+stepsPerLine/2; if(x<0) x=0; if(x>=stepsPerLine) x=stepsPerLine-1; x=Math.floor(x); var oldpixel = error1[x] + get_pixel(turtle.x(),turtle.y()); var newpixel = find_closest_palette_color(oldpixel); var quant_error = oldpixel - newpixel; var xP1 = x+dx; var xM1 = x-dx; if(xP1>=0 && xP1<stepsPerLine) error1[xP1] += quant_error * 7.0/16.0; if(xM1>=0 && xM1<stepsPerLine) error2[xM1] += quant_error * 3.0/16.0; error2[x ] += quant_error * 5.0/16.0; if(xP1>=0 && xP1<stepsPerLine) error2[xP1] += quant_error * 1.0/16.0; if(newpixel>0.5) { turtle.penup(); } else { turtle.pendown(); } turtle.forward(stepSize); if(turtle.x()>=100) { turtle.right(90); turtle.forward(stepSize); turtle.right(90); dx=-dx; newLine(); } else if(turtle.x()<=-100) { turtle.left(90); turtle.forward(stepSize); turtle.left(90); dx=-dx; newLine(); } return turtle.y()<100 && turtle.y()>=-100;//stepsRemaining-- > 0; }