2 #include <math.h> // smallpt, a Path Tracer by Kevin Beason, 2008
4 #include <exec/types.h>
5 #include <aros/debug.h>
6 #include <exec/tasks.h>
7 #include <proto/exec.h>
11 struct Vec
{ // Usage: time ./smallpt 5000 && xv image.ppm
14 double z
; // position, also color (r,g,b)
16 Vec(double x_
= 0, double y_
= 0, double z_
= 0)
23 Vec
operator+(const Vec
&b
) const { return Vec(x
+ b
.x
, y
+ b
.y
, z
+ b
.z
); }
24 Vec
operator-(const Vec
&b
) const { return Vec(x
- b
.x
, y
- b
.y
, z
- b
.z
); }
25 Vec
operator*(double b
) const { return Vec(x
* b
, y
* b
, z
* b
); }
26 Vec
mult(const Vec
&b
) const { return Vec(x
* b
.x
, y
* b
.y
, z
* b
.z
); }
27 Vec
& norm() { return *this = *this * (1/sqrt(x
* x
+ y
* y
+ z
* z
)); }
28 double dot(const Vec
&b
) const { return x
* b
.x
+ y
* b
.y
+ z
* b
.z
; } // cross:
29 Vec
operator%(Vec
&b
){ return Vec(y
* b
.z
- z
* b
.y
, z
* b
.x
- x
* b
.z
, x
* b
.y
- y
* b
.x
);}
34 Ray(Vec o_
, Vec d_
) : o(o_
), d(d_
) {}
42 }; // material types, used in radiance()
47 Vec p
, e
, c
; // position, emission, color
48 Refl_t refl
; // reflection type (DIFFuse, SPECular, REFRactive)
49 Sphere(double rad_
, Vec p_
, Vec e_
, Vec c_
, Refl_t refl_
) : rad(rad_
), p(p_
), e(e_
), c(c_
), refl(refl_
) {}
50 double intersect(const Ray
&r
) const
51 { // returns distance, 0 if nohit
52 Vec op
= p
- r
.o
; // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0
53 double t
, eps
= 1e-4, b
= op
.dot(r
.d
), det
= b
* b
- op
.dot(op
) + rad
* rad
;
58 return (t
= b
- det
) > eps
? t
: ((t
= b
+ det
) > eps
? t
: 0);
62 Sphere spheres
[] = {//Scene: radius, position, emission, color, material
63 Sphere(600, Vec(50,681.6-.27,81.6),Vec(12,12,12), Vec(), DIFF
), //Lite
64 Sphere(1e5
, Vec( 1e5
+1,40.8,81.6), Vec(),Vec(.75,.25,.25),DIFF
),//Left
65 Sphere(1e5
, Vec(-1e5
+99,40.8,81.6),Vec(),Vec(.25,.25,.75),DIFF
),//Rght
66 Sphere(1e5
, Vec(50,40.8, 1e5
), Vec(),Vec(.55,.55,.55),DIFF
),//Back
67 Sphere(1e5
, Vec(50,40.8,-1e5
+170), Vec(),Vec(), DIFF
),//Frnt
68 Sphere(1e5
, Vec(50, 1e5
, 81.6), Vec(),Vec(.75,.75,.75),DIFF
),//Botm
69 Sphere(1e5
, Vec(50,-1e5
+81.6,81.6),Vec(),Vec(.75,.75,.75),DIFF
),//Top
70 Sphere(16.5,Vec(27,16.5,47), Vec(),Vec(.4,.4,.3)*.999, SPEC
),//Mirr
71 Sphere(16.5,Vec(73,16.5,78), Vec(),Vec(.8,.7,.95)*.999, REFR
),//Glas
72 Sphere(10.5,Vec(23,10.5,98), Vec(),Vec(.6,1,0.7)*.999, REFR
),//Glas
73 Sphere(8.,Vec(50,8.,108), Vec(),Vec(1,0.6,0.7)*.999, REFR
),//Glas
74 Sphere(6.5, Vec(53,6.5,48), Vec(),Vec(0.3,.4,.4)*.999, SPEC
),//Mirr
77 const int numSpheres
= sizeof(spheres
)/sizeof(Sphere
);
79 inline double clamp(double x
)
81 return x
<0 ? 0 : x
>1 ? 1 : x
;
84 inline int toInt(double x
)
86 return int(pow(clamp(x
),1/2.2)*255+.5);
89 inline bool intersect(const Ray
&r
, double &t
, int &id
)
91 double n
=numSpheres
, d
, inf
=t
=1e20
;
92 for(int i
=int(n
);i
--;) if((d
=spheres
[i
].intersect(r
))&&d
<t
){t
=d
;id
=i
;}
96 int maximal_ray_depth
= 1000;
98 // ca. 650bytes per ray depth, 650KB stack required for max ray depth of 1000
100 Vec
radiance_expl(const Ray
&r
, int depth
, unsigned short *Xi
,int E
=1){
101 double t
; // distance to intersection
102 int id
=0; // id of intersected object
103 if (!intersect(r
, t
, id
)) return Vec(); // if miss, return black
104 const Sphere
&obj
= spheres
[id
]; // the hit object
105 Vec x
=r
.o
+r
.d
*t
, n
=(x
-obj
.p
).norm(), nl
=n
.dot(r
.d
)<0?n
:n
*-1, f
=obj
.c
;
106 double p
= f
.x
>f
.y
&& f
.x
>f
.z
? f
.x
: f
.y
>f
.z
? f
.y
: f
.z
; // max refl
108 // If depth larger than maximal_ray_depth do not use Russian roulette, give up unconditionally
109 // because AROS does not have automatic stack expansion
110 if (depth
> maximal_ray_depth
)
112 else if (depth
>10||!p
) { // From depth 10 start Russian roulette
113 if (erand48(Xi
)<p
) f
=f
*(1/p
); else return obj
.e
*E
;
115 if (obj
.refl
== DIFF
){ // Ideal DIFFUSE reflection
116 double r1
=2*M_PI
*erand48(Xi
), r2
=erand48(Xi
), r2s
=sqrt(r2
);
117 Vec w
=nl
, u
=((fabs(w
.x
)>.1?Vec(0,1):Vec(1))%w
).norm(), v
=w
%u
;
118 Vec d
= (u
*cos(r1
)*r2s
+ v
*sin(r1
)*r2s
+ w
*sqrt(1-r2
)).norm();
120 // Loop over any lights
122 for (int i
=0; i
<numSpheres
; i
++){
123 const Sphere
&s
= spheres
[i
];
124 if (s
.e
.x
<=0 && s
.e
.y
<=0 && s
.e
.z
<=0) continue; // skip non-lights
126 Vec sw
=s
.p
-x
, su
=((fabs(sw
.x
)>.1?Vec(0,1):Vec(1))%sw
).norm(), sv
=sw
%su
;
127 double cos_a_max
= sqrt(1-s
.rad
*s
.rad
/(x
-s
.p
).dot(x
-s
.p
));
128 double eps1
= erand48(Xi
), eps2
= erand48(Xi
);
129 double cos_a
= 1-eps1
+eps1
*cos_a_max
;
130 double sin_a
= sqrt(1-cos_a
*cos_a
);
131 double phi
= 2*M_PI
*eps2
;
132 Vec l
= su
*cos(phi
)*sin_a
+ sv
*sin(phi
)*sin_a
+ sw
*cos_a
;
134 if (intersect(Ray(x
,l
), t
, id
) && id
==i
){ // shadow ray
135 double omega
= 2*M_PI
*(1-cos_a_max
);
136 e
= e
+ f
.mult(s
.e
*l
.dot(nl
)*omega
)*M_1_PI
; // 1/pi for brdf
140 return obj
.e
*E
+e
+f
.mult(radiance_expl(Ray(x
,d
),depth
,Xi
,0));
141 } else if (obj
.refl
== SPEC
) // Ideal SPECULAR reflection
142 return obj
.e
+ f
.mult(radiance_expl(Ray(x
,r
.d
-n
*2*n
.dot(r
.d
)),depth
,Xi
));
143 Ray
reflRay(x
, r
.d
-n
*2*n
.dot(r
.d
)); // Ideal dielectric REFRACTION
144 bool into
= n
.dot(nl
)>0; // Ray from outside going in?
145 double nc
=1, nt
=1.5, nnt
=into
?nc
/nt
:nt
/nc
, ddn
=r
.d
.dot(nl
), cos2t
;
146 if ((cos2t
=1-nnt
*nnt
*(1-ddn
*ddn
))<0) // Total internal reflection
147 return obj
.e
+ f
.mult(radiance_expl(reflRay
,depth
,Xi
));
148 Vec tdir
= (r
.d
*nnt
- n
*((into
?1:-1)*(ddn
*nnt
+sqrt(cos2t
)))).norm();
149 double a
=nt
-nc
, b
=nt
+nc
, R0
=a
*a
/(b
*b
), c
= 1-(into
?-ddn
:tdir
.dot(n
));
150 double Re
=R0
+(1-R0
)*c
*c
*c
*c
*c
,Tr
=1-Re
,P
=.25+.5*Re
,RP
=Re
/P
,TP
=Tr
/(1-P
);
151 return obj
.e
+ f
.mult(depth
>2 ? (erand48(Xi
)<P
? // Russian roulette
152 radiance_expl(reflRay
,depth
,Xi
)*RP
:radiance_expl(Ray(x
,tdir
),depth
,Xi
)*TP
) :
153 radiance_expl(reflRay
,depth
,Xi
)*Re
+radiance_expl(Ray(x
,tdir
),depth
,Xi
)*Tr
);
156 Vec
radiance(const Ray
&r
, int depth
, unsigned short *Xi
)
158 double t
; // distance to intersection
159 int id
=0; // id of intersected object
161 if (!intersect(r
, t
, id
))
162 return Vec(); // if miss, return black
164 const Sphere
&obj
= spheres
[id
]; // the hit object
166 Vec x
=r
.o
+r
.d
*t
, n
=(x
-obj
.p
).norm(), nl
=n
.dot(r
.d
)<0?n
:n
*-1, f
=obj
.c
;
169 // Above maximal_ray_depth break recursive loop unconditionally
170 if (depth
> maximal_ray_depth
)
172 else if (depth
>5) // From depth of 5 start Russian roulette
174 double p
= f
.x
>f
.y
&& f
.x
>f
.z
? f
.x
: f
.y
>f
.z
? f
.y
: f
.z
; // max refl
178 else return obj
.e
; //R.R.
180 if (obj
.refl
== DIFF
){ // Ideal DIFFUSE reflection
181 double r1
=2*M_PI
*erand48(Xi
), r2
=erand48(Xi
), r2s
=sqrt(r2
);
182 Vec w
=nl
, u
=((fabs(w
.x
)>.1?Vec(0,1):Vec(1))%w
).norm(), v
=w
%u
;
183 Vec d
= (u
*cos(r1
)*r2s
+ v
*sin(r1
)*r2s
+ w
*sqrt(1-r2
)).norm();
184 return obj
.e
+ f
.mult(radiance(Ray(x
,d
),depth
,Xi
));
185 } else if (obj
.refl
== SPEC
) // Ideal SPECULAR reflection
186 return obj
.e
+ f
.mult(radiance(Ray(x
,r
.d
-n
*2*n
.dot(r
.d
)),depth
,Xi
));
187 Ray
reflRay(x
, r
.d
-n
*2*n
.dot(r
.d
)); // Ideal dielectric REFRACTION
188 bool into
= n
.dot(nl
)>0; // Ray from outside going in?
189 double nc
=1, nt
=1.5, nnt
=into
?nc
/nt
:nt
/nc
, ddn
=r
.d
.dot(nl
), cos2t
;
190 if ((cos2t
=1-nnt
*nnt
*(1-ddn
*ddn
))<0) // Total internal reflection
191 return obj
.e
+ f
.mult(radiance(reflRay
,depth
,Xi
));
192 Vec tdir
= (r
.d
*nnt
- n
*((into
?1:-1)*(ddn
*nnt
+sqrt(cos2t
)))).norm();
193 double a
=nt
-nc
, b
=nt
+nc
, R0
=a
*a
/(b
*b
), c
= 1-(into
?-ddn
:tdir
.dot(n
));
194 double Re
=R0
+(1-R0
)*c
*c
*c
*c
*c
,Tr
=1-Re
,P
=.25+.5*Re
,RP
=Re
/P
,TP
=Tr
/(1-P
);
195 return obj
.e
+ f
.mult(depth
>2 ? (erand48(Xi
)<P
? // Russian roulette
196 radiance(reflRay
,depth
,Xi
)*RP
:radiance(Ray(x
,tdir
),depth
,Xi
)*TP
) :
197 radiance(reflRay
,depth
,Xi
)*Re
+radiance(Ray(x
,tdir
),depth
,Xi
)*Tr
);
200 static inline struct MyMessage
*AllocMsg(struct MinList
*msgPool
)
202 if (IsMinListEmpty(msgPool
))
203 bug("!!! smallpt.cc - run out of free messages!\n");
205 return (struct MyMessage
*)REMHEAD(msgPool
);
209 ULONG
*ptr
= (ULONG
*)FindTask(NULL
)->tc_SPLower
;
212 asm volatile("mov %%rsp,%0":"=r"(rsp
));
220 ULONG
*ptr
= (ULONG
*)FindTask(NULL
)->tc_SPLower
;
221 IPTR top
= (IPTR
)FindTask(NULL
)->tc_SPUpper
;
223 while(*ptr
++ == 0xdeadbeef);
225 bug("--> USED STACK: %d\n", top
- (IPTR
)ptr
);
228 extern "C" void RenderTile(struct ExecBase
*SysBase
, struct MsgPort
*masterPort
, struct MsgPort
**myPort
)
231 struct MyMessage
*msg
;
233 struct MsgPort
*port
= CreateMsgPort();
234 struct MsgPort
*syncPort
= CreateMsgPort();
235 struct MinList msgPool
;
237 c
= (Vec
*)AllocMem(sizeof(Vec
) * TILE_SIZE
* TILE_SIZE
, MEMF_ANY
| MEMF_CLEAR
);
241 FreeSignal(syncPort
->mp_SigBit
);
242 syncPort
->mp_SigBit
= -1;
243 syncPort
->mp_Flags
= PA_IGNORE
;
247 D(bug("[SMP-SmallPT-Task] hello, msgport=%p\n", port
));
249 msg
= (struct MyMessage
*)AllocMem(sizeof(struct MyMessage
) * 20, MEMF_PUBLIC
| MEMF_CLEAR
);
250 for (int i
=0; i
< 20; i
++)
251 ADDHEAD(&msgPool
, &msg
[i
]);
259 m
= AllocMsg(&msgPool
);
261 /* Tell renderer that we are bored and want to do some work */
262 m
->mm_Message
.mn_Length
= sizeof(msg
);
263 m
->mm_Message
.mn_ReplyPort
= port
;
264 m
->mm_Type
= MSG_HUNGRY
;
265 PutMsg(masterPort
, &m
->mm_Message
);
267 D(bug("[SMP-SmallPT-Task] Just told renderer I'm hungry\n"));
270 signals
= Wait(SIGBREAKF_CTRL_C
| (1 << port
->mp_SigBit
));
272 if (signals
& (1 << port
->mp_SigBit
))
274 while ((m
= (struct MyMessage
*)GetMsg(port
)))
276 if (m
->mm_Message
.mn_Node
.ln_Type
== NT_REPLYMSG
)
278 ADDHEAD(&msgPool
, m
);
283 if (m
->mm_Type
== MSG_DIE
)
286 ReplyMsg(&m
->mm_Message
);
288 else if (m
->mm_Type
== MSG_RENDERTILE
)
290 struct tileWork
*tile
= m
->mm_Body
.RenderTile
.tile
;
291 int w
= m
->mm_Body
.RenderTile
.width
;
292 int h
= m
->mm_Body
.RenderTile
.height
;
293 int samps
= m
->mm_Body
.RenderTile
.numberOfSamples
;
294 ULONG
*buffer
= m
->mm_Body
.RenderTile
.buffer
;
295 int tile_x
= m
->mm_Body
.RenderTile
.tile
->x
;
296 int tile_y
= m
->mm_Body
.RenderTile
.tile
->y
;
297 struct MsgPort
*guiPort
= m
->mm_Body
.RenderTile
.guiPort
;
298 int explicit_mode
= m
->mm_Body
.RenderTile
.explicitMode
;
301 spheres
[0] = Sphere(5, Vec(50,81.6-16.5,81.6),Vec(4,4,4)*20, Vec(), DIFF
);
303 spheres
[0] = Sphere(600, Vec(50,681.6-.27,81.6),Vec(12,12,12), Vec(), DIFF
);
305 ReplyMsg(&m
->mm_Message
);
309 Ray
cam(Vec(50, 52, 295.6), Vec(0, -0.042612, -1).norm()); // cam pos, dir
310 Vec cx
= Vec(w
* .5135 / h
), cy
= (cx
% cam
.d
).norm() * .5135, r
;
312 for (int i
=0; i
< TILE_SIZE
* TILE_SIZE
; i
++)
315 for (int _y
=tile_y
* 32; _y
< (tile_y
+ 1) * 32; _y
++)
318 for (unsigned short _x
=tile_x
* 32, Xi
[3]={0,0,(UWORD
)(y
*y
*y
)}; _x
< (tile_x
+ 1) * 32; _x
++) // Loop cols
320 int x
= _x
; // w - _x - 1;
321 for (int sy
=0, i
=(_y
-tile_y
*32)*32+_x
-tile_x
*32; sy
<2; sy
++) // 2x2 subpixel rows
323 for (int sx
=0; sx
<2; sx
++, r
=Vec())
324 { // 2x2 subpixel cols
325 for (int s
=0; s
<samps
; s
++)
327 double r1
=2*erand48(Xi
), dx
=r1
<1 ? sqrt(r1
)-1: 1-sqrt(2-r1
);
328 double r2
=2*erand48(Xi
), dy
=r2
<1 ? sqrt(r2
)-1: 1-sqrt(2-r2
);
329 Vec d
= cx
*( ( (sx
+.5 + dx
)/2 + x
)/w
- .5) +
330 cy
*( ( (sy
+.5 + dy
)/2 + y
)/h
- .5) + cam
.d
;
332 r
= r
+ radiance_expl(Ray(cam
.o
+d
*140,d
.norm()),0,Xi
)*(1./samps
);
334 r
= r
+ radiance(Ray(cam
.o
+d
*140,d
.norm()),0,Xi
)*(1./samps
);
335 } // Camera rays are pushed ^^^^^ forward to start in interior
336 c
[i
] = c
[i
] + Vec(clamp(r
.x
),clamp(r
.y
),clamp(r
.z
))*.25;
340 int start_ptr
= tile_y
*32*w
+ tile_x
*32;
341 for (int yy
=0; yy
< 32; yy
++)
343 for (int xx
=0; xx
< 32; xx
++)
345 buffer
[start_ptr
+xx
] = ((toInt(c
[(xx
+32*yy
)].z
) & 0xff) << 24) |
346 ((toInt(c
[(xx
+32*yy
)].y
) & 0xff) << 16) | ((toInt(c
[(xx
+ 32*yy
)].x
) & 0xff) << 8) | 0xff;
354 m
= AllocMsg(&msgPool
);
355 m
->mm_Message
.mn_Length
= sizeof(struct MyMessage
);
356 m
->mm_Message
.mn_ReplyPort
= syncPort
;
357 m
->mm_Type
= MSG_REDRAWTILE
;
358 m
->mm_Body
.RedrawTile
.TileX
= tile_x
;
359 m
->mm_Body
.RedrawTile
.TileY
= tile_y
;
360 PutMsg(guiPort
, &m
->mm_Message
);
363 else if ((m
= (struct MyMessage
*)GetMsg(syncPort
)))
365 ADDHEAD(&msgPool
, m
);
370 Signal((struct Task
*)guiPort
->mp_SigTask
, SIGBREAKF_CTRL_D
);
374 Signal((struct Task
*)guiPort
->mp_SigTask
, SIGBREAKF_CTRL_D
);
376 m
= AllocMsg(&msgPool
);
377 m
->mm_Message
.mn_Length
= sizeof(struct MyMessage
);
378 m
->mm_Message
.mn_ReplyPort
= port
;
379 m
->mm_Type
= MSG_RENDERREADY
;
380 m
->mm_Body
.RenderTile
.tile
= tile
;
381 PutMsg(masterPort
, &m
->mm_Message
);
383 m
= AllocMsg(&msgPool
);
384 m
->mm_Message
.mn_Length
= sizeof(struct MyMessage
);
385 m
->mm_Message
.mn_ReplyPort
= port
;
386 m
->mm_Type
= MSG_HUNGRY
;
387 PutMsg(masterPort
, &m
->mm_Message
);
393 if (signals
& SIGBREAKB_CTRL_C
)
398 D(bug("[SMP-SmallPT-Task] cleaning up stuff\n"));
399 FreeMem(msg
, sizeof(struct MyMessage
) * 20);
401 DeleteMsgPort(syncPort
);
403 FreeMem(c
, sizeof(Vec
) * TILE_SIZE
* TILE_SIZE
);