1 ! Factor port of the raytracer benchmark from
2 ! http://www.ffconsultancy.com/free/ray_tracer/languages.html
4 USING: arrays accessors specialized-arrays.double io io.files
5 io.files.temp io.encodings.binary kernel math math.constants
6 math.functions math.vectors math.parser make sequences
7 sequences.private words hints ;
8 IN: benchmark.raytracer
12 ! Normalized { -1 -3 2 }.
20 CONSTANT: oversampling 4
26 : delta ( -- n ) epsilon sqrt ; inline
28 TUPLE: ray { orig double-array read-only } { dir double-array read-only } ;
32 TUPLE: hit { normal double-array read-only } { lambda float read-only } ;
36 GENERIC: intersect-scene ( hit ray scene -- hit )
38 TUPLE: sphere { center double-array read-only } { radius float read-only } ;
42 : sphere-v ( sphere ray -- v )
43 [ center>> ] [ orig>> ] bi* v- ; inline
45 : sphere-b ( v ray -- b )
48 : sphere-d ( sphere b v -- d )
49 [ radius>> sq ] [ sq ] [ norm-sq ] tri* - + ; inline
51 : -+ ( x y -- x-y x+y )
52 [ - ] [ + ] 2bi ; inline
54 : sphere-t ( b d -- t )
56 [ 2drop 1/0. ] [ [ [ 0.0 > ] keep ] dip ? ] if ; inline
58 : sphere-b&v ( sphere ray -- b v )
59 [ sphere-v ] [ nip ] 2bi
60 [ sphere-b ] [ drop ] 2bi ; inline
62 : ray-sphere ( sphere ray -- t )
63 [ drop ] [ sphere-b&v ] 2bi
64 [ drop ] [ sphere-d ] 3bi
65 dup 0.0 < [ 3drop 1/0. ] [ sqrt sphere-t nip ] if ; inline
67 : if-ray-sphere ( hit ray sphere quot -- hit )
68 #! quot: hit ray sphere l -- hit
70 [ ] [ swap ray-sphere nip ] [ 2drop lambda>> ] 3tri
72 ] dip [ 3drop ] if ; inline
74 : sphere-n ( ray sphere l -- n )
75 [ [ orig>> ] [ dir>> ] bi ] [ center>> ] [ ] tri*
76 swap [ v*n ] dip v- v+ ; inline
78 M: sphere intersect-scene ( hit ray sphere -- hit )
79 [ [ sphere-n normalize ] keep <hit> nip ] if-ray-sphere ;
81 HINTS: M\ sphere intersect-scene { hit ray sphere } ;
83 TUPLE: group < sphere { objs array read-only } ;
85 : <group> ( objs bound -- group )
86 [ center>> ] [ radius>> ] bi rot group boa ; inline
88 : make-group ( bound quot -- )
89 swap [ { } make ] dip <group> ; inline
91 M: group intersect-scene ( hit ray group -- hit )
92 [ drop objs>> [ intersect-scene ] with each ] if-ray-sphere ;
94 HINTS: M\ group intersect-scene { hit ray group } ;
96 CONSTANT: initial-hit T{ hit f double-array{ 0.0 0.0 0.0 } 1/0. }
98 : initial-intersect ( ray scene -- hit )
99 [ initial-hit ] 2dip intersect-scene ; inline
101 : ray-o ( ray hit -- o )
102 [ [ orig>> ] [ normal>> delta v*n ] bi* ]
103 [ [ dir>> ] [ lambda>> ] bi* v*n ]
106 : sray-intersect ( ray scene hit -- ray )
107 swap [ ray-o light vneg <ray> ] dip initial-intersect ; inline
109 : ray-g ( hit -- g ) normal>> light v. ; inline
111 : cast-ray ( ray scene -- g )
112 2dup initial-intersect dup lambda>> 1/0. = [
115 [ sray-intersect lambda>> 1/0. = ] keep swap
116 [ ray-g neg ] [ drop 0.0 ] if
119 : create-center ( c r d -- c2 )
120 [ 3.0 12.0 sqrt / * ] dip n*v v+ ; inline
122 DEFER: create ( level c r -- scene )
124 : create-step ( level c r d -- scene )
125 over [ create-center ] dip 2.0 / [ 1 - ] 2dip create ;
127 : create-offsets ( quot -- )
129 double-array{ -1.0 1.0 -1.0 }
130 double-array{ 1.0 1.0 -1.0 }
131 double-array{ -1.0 1.0 1.0 }
132 double-array{ 1.0 1.0 1.0 }
135 : create-bound ( c r -- sphere ) 3.0 * <sphere> ;
137 : create-group ( level c r -- scene )
140 [ [ 3dup ] dip create-step , ] create-offsets 3drop
143 : create ( level c r -- scene )
144 pick 1 = [ <sphere> nip ] [ create-group ] if ;
146 : ss-point ( dx dy -- point )
147 [ oversampling /f ] bi@ 0.0 double-array{ } 3sequence ;
149 : ss-grid ( -- ss-grid )
150 oversampling [ oversampling [ ss-point ] with map ] map ;
152 : ray-grid ( point ss-grid -- ray-grid )
154 [ v+ normalize double-array{ 0.0 0.0 -4.0 } swap <ray> ] with map
157 : ray-pixel ( scene point -- n )
158 ss-grid ray-grid [ 0.0 ] 2dip
159 [ [ swap cast-ray + ] with each ] with each ;
161 : pixel-grid ( -- grid )
164 [ size 0.5 * - ] bi@ swap size
165 double-array{ } 3sequence
169 : pgm-header ( w h -- )
170 "P5\n" % swap # " " % # "\n255\n" % ;
172 : pgm-pixel ( n -- ) 255 * 0.5 + >fixnum , ;
174 : ray-trace ( scene -- pixels )
175 pixel-grid [ [ ray-pixel ] with map ] with map ;
178 levels double-array{ 0.0 -1.0 0.0 } 1.0 create ray-trace [
180 [ [ oversampling sq / pgm-pixel ] each ] each
183 : raytracer-main ( -- )
184 run "raytracer.pnm" temp-file binary set-file-contents ;