1 ! Factor port of the raytracer benchmark from
2 ! http://www.ffconsultancy.com/free/ray_tracer/languages.html
4 USING: arrays compiler generic io kernel math namespaces
10 #! Normalized { -1 -3 2 }.
11 { -0.2672612419124244 -0.8017837257372732 0.5345224838248488 } ; inline
13 : oversampling 4 ; inline
19 : delta 1.4901161193847656E-8 ; inline
23 TUPLE: hit normal lambda ;
25 GENERIC: intersect-scene ( hit ray scene -- hit )
27 TUPLE: sphere center radius ;
29 : sphere-v ( sphere ray -- v )
30 swap sphere-center swap ray-orig v- ; inline
32 : sphere-b ( ray v -- b ) swap ray-dir v. ; inline
34 : sphere-disc ( sphere v b -- d )
35 sq swap norm-sq - swap sphere-radius sq + ; inline
37 : -+ ( x y -- x-y x+y ) [ - ] 2keep + ; inline
39 : sphere-b/d ( b d -- t )
40 -+ dup 0.0 < [ 2drop 1.0/0.0 ] [ >r [ 0.0 > ] keep r> ? ] if ; inline
42 : ray-sphere ( sphere ray -- t )
43 2dup sphere-v tuck sphere-b [ sphere-disc ] keep
44 over 0.0 < [ 2drop 1.0/0.0 ] [ swap sqrt sphere-b/d ] if ;
47 : sphere-n ( ray sphere l -- n )
48 pick ray-dir n*v swap sphere-center v- swap ray-orig v+ ;
51 : if-ray-sphere ( hit ray sphere quot -- hit )
52 #! quot: hit ray sphere l -- hit
53 >r pick hit-lambda >r 2dup swap ray-sphere dup r> >=
54 [ 3drop ] r> if ; inline
56 M: sphere intersect-scene ( hit ray sphere -- hit )
57 [ [ sphere-n normalize ] keep <hit> nip ] if-ray-sphere ;
61 C: group ( objs bound -- group )
62 [ set-delegate ] keep [ set-group-objs ] keep ;
64 : make-group ( bound quot -- )
65 swap >r { } make r> <group> ; inline
67 M: group intersect-scene ( hit ray group -- hit )
70 group-objs [ >r tuck r> intersect-scene swap ] each
74 : initial-hit T{ hit f { 0.0 0.0 0.0 } 1.0/0.0 } ; inline
76 : initial-intersect ( ray scene -- hit )
77 initial-hit -rot intersect-scene ; inline
79 : ray-o ( ray hit -- o )
80 over ray-dir over hit-lambda v*n
81 swap hit-normal delta v*n v+
82 swap ray-orig v+ ; inline
84 : sray-intersect ( ray scene hit -- ray )
85 swap >r ray-o light vneg <ray> r> initial-intersect ; inline
87 : ray-g ( hit -- g ) hit-normal light v. ; inline
89 : cast-ray ( ray scene -- g )
90 2dup initial-intersect dup hit-lambda 1.0/0.0 = [
93 dup ray-g >r sray-intersect hit-lambda 1.0/0.0 =
94 [ r> neg ] [ r> drop 0.0 ] if
97 : create-center ( c r d -- c2 )
98 >r 3.0 12.0 sqrt / * r> n*v v+ ; inline
100 DEFER: create ( level c r -- scene )
102 : create-step ( level c r d -- scene )
103 over >r create-center r> 2.0 / >r >r 1 - r> r> create ;
105 : create-offsets ( quot -- )
113 : create-bound ( c r -- sphere ) 3.0 * <sphere> ;
115 : create-group ( level c r -- scene )
118 [ >r 3dup r> create-step , ] create-offsets 3drop
121 : create ( level c r -- scene )
122 pick 1 = [ <sphere> nip ] [ create-group ] if ;
124 : ss-point ( dx dy -- point )
125 [ oversampling /f ] 2apply 0.0 3array ;
127 : ss-grid ( -- ss-grid )
128 oversampling [ oversampling [ ss-point ] map-with ] map ;
130 : ray-grid ( point ss-grid -- ray-grid )
132 [ v+ normalize { 0.0 0.0 -4.0 } swap <ray> ] map-with
135 : ray-pixel ( scene point -- n )
136 ss-grid ray-grid 0.0 -rot
137 [ [ swap cast-ray + ] each-with ] each-with ;
139 : pixel-grid ( -- grid )
142 [ size 0.5 * - ] 2apply swap size >float 3array
146 : pgm-header ( w h -- )
147 "P5\n" % swap # " " % # "\n255\n" % ;
149 : pgm-pixel ( n -- ) 255 * 0.5 + >fixnum , ;
151 : ray-trace ( scene -- pixels )
152 pixel-grid [ [ ray-pixel ] map-with ] map-with ;
155 levels { 0.0 -1.0 0.0 } 1.0 create ray-trace [
157 [ [ oversampling sq / pgm-pixel ] each ] each
160 : run>file ( file -- )
161 "Generating " write dup write "..." print
162 <file-writer> [ run write ] with-stream ;
164 PROVIDE: apps/raytracer ;
166 MAIN: apps/raytracer [ "raytracer.pnm" run>file ] time ;