Alla yksinkertainen säteenseuranta ohjelma 8th ohjelmointikielellä. Tuki useammalle laskenta säikeelle vielä lisäämättä.
\ \ Simple toy ray tracer \ true app:isgui ! var gui : WIDTH 512 ; : HEIGHT 512 ; WIDTH HEIGHT img:new var, imgbuf : SPHERE 0 ; : PLANE 1 ; [ 0.0, 0.0, 0.0 ] [1,3] mat:new var, origin [ -0.6, 0.9, 3.1 ] [1,3] mat:new var, point-light 0.1 var, ambient [ { type:0, xyz:[ -0.2, -0.8, 3.8 ], radius:0.7, color:[ 210, 50, 50 ], mirror:false }, { type:0, xyz:[ 0.6, 0.5, 3.5 ], radius:0.5, color:[ 255, 255, 255 ], mirror:true }, { type:1, axis:0, distance:1.5, color:[ 100, 100, 255 ], mirror:false }, { type:1, axis:1, distance:-1.5, color:[ 100, 255, 100 ], mirror:false }, { type:1, axis:0, distance:-1.5, color:[ 100, 100, 100 ], mirror:false }, { type:1, axis:1, distance:1.5, color:[ 100, 255, 255 ], mirror:false }, { type:1, axis:2, distance:5.0, color:[ 255, 100, 255 ], mirror:false } ] var, primitives : magnitude \ mat -- mat mat:data nip ' n:sqr a:map ' n:+ 0 a:reduce n:sqrt ; : normalize \ mat -- mat dup magnitude >r mat:data nip ( r@ n:/ ) a:map rdrop [1,3] mat:new ; : dot3 \ m1 m2 -- n mat:trans mat:* 0 mat:get-n nip ; : sub3 \ m1 m2 -- m3 mat:data nip ' n:neg a:map [1,3] mat:new mat:+ ; : sphere-normal \ point -- point primitives @ "index" t:@ a:@ nip "xyz" m:@ nip [1,3] mat:new sub3 normalize ; : plane-normal \ point -- point primitives @ "index" t:@ a:@ nip "axis" m:@ nip n:int >r [ 0.0, 0.0, 0.0 ] const swap mat:data nip r@ a:@ nip primitives @ "index" t:@ a:@ nip "distance" m:@ nip n:- r> swap a:! [1,3] mat:new normalize ; : light-diffuse \ normal point -- n point-light @ swap sub3 normalize dot3 ; : light-sphere \ -- n "point" t:@ sphere-normal "point" t:@ light-diffuse ; : light-plane \ -- n point-light @ plane-normal "point" t:@ light-diffuse ; : light-primitive primitives @ "index" t:@ a:@ nip "type" m:@ nip [ ' light-sphere , ' light-plane ] swap caseof ambient @ n:max 1.0 swap n:min ; : reflect \ ray -- ray >r primitives @ "index" t:@ a:@ nip "type" m:@ nip 0 n:= if "point" t:@ sphere-normal else origin @ plane-normal then dup r@ swap dot3 2 n:* mat:n* r> swap sub3 normalize ; : check-distance \ distance index swap dup >r "distance" t:@ n:< r@ 0.0 n:> and if "index" t:! r@ "distance" t:! true "intersect" t:! else drop then rdrop ; locals: : ray-sphere \ origin ray index -- dup "index" w:! swap >r primitives @ swap a:@ nip "xyz" m:@ nip [1,3] mat:new swap sub3 "s" w:! r@ r@ dot3 "A" w:! -2.0 "s" w:@ r> dot3 n:* "B" w:! "s" w:@ dup dot3 primitives @ "index" w:@ a:@ nip "radius" m:@ nip dup n:* n:- "C" w:! "B" w:@ dup n:* 4.0 "A" w:@ n:* "C" w:@ n:* n:- "D" w:! "D" w:@ 0.0 n:> if "C" w:@ -0.00001 n:< if 1.0 else -1.0 then \ sign on TOS "D" w:@ n:sqrt n:* "B" w:@ n:neg n:+ 2.0 "A" w:@ n:* n:/ "index" w:@ check-distance then ; locals: : ray-plane \ origin ray index -- "index" w:! "ray" w:! primitives @ "index" w:@ a:@ nip "axis" m:@ nip n:int >r "ray" w:@ mat:data nip r@ a:@ nip 0.0 n:= not if primitives @ "index" w:@ a:@ nip "distance" m:@ nip swap mat:data nip r@ a:@ nip n:- "ray" w:@ mat:data nip r@ a:@ nip n:/ "index" w:@ check-distance else drop then rdrop ; : ray-primitive \ origin ray index -- dup primitives @ swap a:@ nip "type" m:@ nip [ ' ray-sphere , ' ray-plane ] swap caseof ; : raytrace \ origin ray -- false "intersect" t:! 999999.9 "distance" t:! ( >r 2dup r> ray-primitive ) 0 primitives @ a:len nip n:1- loop 2drop ; : compute-pixel-color \ origin ray -- color dup >r raytrace "intersect" t:@ if r@ const "distance" t:@ mat:n* "point" t:! \ Test if ray hit mirror object. \ Reflect ray off the surface and follow the reflected ray. primitives @ "index" t:@ a:@ nip "mirror" m:@ nip if r@ reflect dup "point" t:@ swap raytrace "intersect" t:@ if "distance" t:@ mat:n* "point" t:@ mat:+ "point" t:! else drop then then \ We follow the ray from \ point light into the object. If it hits some other object first, \ we know we are in shadow (use ambient color of original object). \ Else we need to compute the lighting (diffuse + ambient). "index" t:@ >r point-light @ "point" t:@ point-light @ sub3 raytrace r@ "index" t:@ n:= if light-primitive else ambient @ then dup dup 3 a:close primitives @ r> a:@ nip "color" m:@ nip ( n:* n:int ) a:2map 255 a:push else ambient @ dup dup 3 a:close [ 255, 255, 255 ] ( n:* n:int ) a:2map 255 a:push then rdrop ; : render -1 sleep ( imgbuf lock drop ( dup >r WIDTH n:/ 0.5 n:- over HEIGHT n:/ 0.5 n:- n:neg 1.0 3 a:close [1,3] mat:new origin @ swap compute-pixel-color >r imgbuf @ over r> r> swap img:pix! drop ) 0 WIDTH n:1- loop imgbuf unlock drop gui @ g:invalidate 2drop \ wait for the draw to complete before resuming -1 sleep ) 0 HEIGHT n:1- loop ; : notify-renderer \ gui -- "task" g:m@ nip t:notify ; : do-draw imgbuf lock @ 0 0 g:image-at imgbuf unlock drop notify-renderer ; : app:main \ Start the update task, but it will sleep until the GUI is ready: ' render t:task \ Define our GUI... { kind: "win", title: "Simple Toy Ray Tracer", wide: 512, high: 512, nobg: true, center: true, resizable: false, font: "Arial 10", \ Gets invoked when the GUI is present. Save the new gui handle, and start \ the update task (outside this callback): init: ( gui ! notify-renderer ) , draw: "do-draw" } \ And save the task in our GUI map: "task" rot m:! \ Create the GUI (don't have to save the gui item, we do that in 'init'): g:new ;
Aihe on jo aika vanha, joten et voi enää vastata siihen.