| rev |
line source |
|
msach@0
|
1 /* c-ray-mt - a simple multithreaded raytracing filter.
|
|
msach@0
|
2 * Copyright (C) 2006 John Tsiombikas <nuclear@siggraph.org>
|
|
msach@0
|
3 *
|
|
msach@0
|
4 * You are free to use, modify and redistribute this program under the
|
|
msach@0
|
5 * terms of the GNU General Public License v2 or (at your option) later.
|
|
msach@0
|
6 * see "http://www.gnu.org/licenses/gpl.txt" for details.
|
|
msach@0
|
7 * ---------------------------------------------------------------------
|
|
msach@0
|
8 * Usage:
|
|
msach@0
|
9 * compile: just type make
|
|
msach@0
|
10 * (add any arch-specific optimizations for your compiler in CFLAGS first)
|
|
msach@0
|
11 * run: cat scene | ./c-ray-mt [-t num-threads] >foo.ppm
|
|
msach@0
|
12 * (on broken systems such as windows try: c-ray-mt -i scene -o foo.ppm)
|
|
msach@0
|
13 * enjoy: display foo.ppm
|
|
msach@0
|
14 * (with imagemagick, or use your favorite image viewer)
|
|
msach@0
|
15 * ---------------------------------------------------------------------
|
|
msach@0
|
16 * Scene file format:
|
|
msach@0
|
17 * # sphere (many)
|
|
msach@0
|
18 * s x y z rad r g b shininess reflectivity
|
|
msach@0
|
19 * # light (many)
|
|
msach@0
|
20 * l x y z
|
|
msach@0
|
21 * # camera (one)
|
|
msach@0
|
22 * c x y z fov tx ty tz
|
|
msach@0
|
23 * ---------------------------------------------------------------------
|
|
msach@0
|
24 */
|
|
msach@0
|
25 #include <stdio.h>
|
|
msach@0
|
26 #include <stdlib.h>
|
|
msach@0
|
27 #include <string.h>
|
|
msach@0
|
28 #include <math.h>
|
|
msach@0
|
29 #include <ctype.h>
|
|
msach@0
|
30 #include <errno.h>
|
|
msach@0
|
31 #include <pthread.h>
|
|
msach@0
|
32 #include "SSR_lib/SSR.h"
|
|
msach@0
|
33
|
|
msach@0
|
34 #define VER_MAJOR 1
|
|
msach@0
|
35 #define VER_MINOR 1
|
|
msach@0
|
36 #define VER_STR "c-ray-mt v%d.%d\n"
|
|
msach@0
|
37
|
|
msach@0
|
38 #if !defined(unix) && !defined(__unix__)
|
|
msach@0
|
39 #ifdef __MACH__
|
|
msach@0
|
40 #define unix 1
|
|
msach@0
|
41 #define __unix__ 1
|
|
msach@0
|
42 #endif /* __MACH__ */
|
|
msach@0
|
43 #endif /* unix */
|
|
msach@0
|
44
|
|
msach@0
|
45 /* find the appropriate way to define explicitly sized types */
|
|
msach@0
|
46 /* for C99 or GNU libc (also mach's libc) we can use stdint.h */
|
|
msach@0
|
47 #if (__STDC_VERSION__ >= 199900) || defined(__GLIBC__) || defined(__MACH__)
|
|
msach@0
|
48 #include <stdint.h>
|
|
msach@0
|
49 #elif defined(unix) || defined(__unix__) /* some UNIX systems have them in sys/types.h */
|
|
msach@0
|
50 #include <sys/types.h>
|
|
msach@0
|
51 #elif defined(__WIN32__) || defined(WIN32) /* the nameless one */
|
|
msach@0
|
52 typedef unsigned __int8 uint8_t;
|
|
msach@0
|
53 typedef unsigned __int32 uint32_t;
|
|
msach@0
|
54 #endif /* sized type detection */
|
|
msach@0
|
55
|
|
msach@0
|
56 struct vec3 {
|
|
msach@0
|
57 double x, y, z;
|
|
msach@0
|
58 };
|
|
msach@0
|
59
|
|
msach@0
|
60 struct ray {
|
|
msach@0
|
61 struct vec3 orig, dir;
|
|
msach@0
|
62 };
|
|
msach@0
|
63
|
|
msach@0
|
64 struct material {
|
|
msach@0
|
65 struct vec3 col; /* color */
|
|
msach@0
|
66 double spow; /* specular power */
|
|
msach@0
|
67 double refl; /* reflection intensity */
|
|
msach@0
|
68 };
|
|
msach@0
|
69
|
|
msach@0
|
70 struct sphere {
|
|
msach@0
|
71 struct vec3 pos;
|
|
msach@0
|
72 double rad;
|
|
msach@0
|
73 struct material mat;
|
|
msach@0
|
74 struct sphere *next;
|
|
msach@0
|
75 };
|
|
msach@0
|
76
|
|
msach@0
|
77 struct spoint {
|
|
msach@0
|
78 struct vec3 pos, normal, vref; /* position, normal and view reflection */
|
|
msach@0
|
79 double dist; /* parametric distance of intersection along the ray */
|
|
msach@0
|
80 };
|
|
msach@0
|
81
|
|
msach@0
|
82 struct camera {
|
|
msach@0
|
83 struct vec3 pos, targ;
|
|
msach@0
|
84 double fov;
|
|
msach@0
|
85 };
|
|
msach@0
|
86
|
|
msach@0
|
87 struct procr_data {
|
|
msach@0
|
88 VirtProcr *VP;
|
|
msach@0
|
89 VirtProcr *parentVP;
|
|
msach@0
|
90 int sl_start, sl_count;
|
|
msach@0
|
91 uint32_t *pixels;
|
|
msach@0
|
92 };
|
|
msach@0
|
93 typedef struct procr_data procr_data;
|
|
msach@0
|
94
|
|
msach@0
|
95 void render_scanline(int xsz, int ysz, int sl, uint32_t *fb, int samples);
|
|
msach@0
|
96 struct vec3 trace(struct ray ray, int depth);
|
|
msach@0
|
97 struct vec3 shade(struct sphere *obj, struct spoint *sp, int depth);
|
|
msach@0
|
98 struct vec3 reflect(struct vec3 v, struct vec3 n);
|
|
msach@0
|
99 struct vec3 cross_product(struct vec3 v1, struct vec3 v2);
|
|
msach@0
|
100 struct ray get_primary_ray(int x, int y, int sample);
|
|
msach@0
|
101 struct vec3 get_sample_pos(int x, int y, int sample);
|
|
msach@0
|
102 struct vec3 jitter(int x, int y, int s);
|
|
msach@0
|
103 int ray_sphere(const struct sphere *sph, struct ray ray, struct spoint *sp);
|
|
msach@0
|
104 void load_scene(FILE *fp);
|
|
msach@0
|
105 unsigned long get_msec(void);
|
|
msach@0
|
106
|
|
msach@0
|
107 void thread_func(void *tdata, VirtProcr *VProc);
|
|
msach@0
|
108
|
|
msach@0
|
109 #define MAX_LIGHTS 16 /* maximum number of lights */
|
|
msach@0
|
110 #define RAY_MAG 1000.0 /* trace rays of this magnitude */
|
|
msach@0
|
111 #define MAX_RAY_DEPTH 5 /* raytrace recursion limit */
|
|
msach@0
|
112 #define FOV 0.78539816 /* field of view in rads (pi/4) */
|
|
msach@0
|
113 #define HALF_FOV (FOV * 0.5)
|
|
msach@0
|
114 #define ERR_MARGIN 1e-6 /* an arbitrary error margin to avoid surface acne */
|
|
msach@0
|
115
|
|
msach@0
|
116 /* bit-shift ammount for packing each color into a 32bit uint */
|
|
msach@0
|
117 #ifdef LITTLE_ENDIAN
|
|
msach@0
|
118 #define RSHIFT 16
|
|
msach@0
|
119 #define BSHIFT 0
|
|
msach@0
|
120 #else /* big endian */
|
|
msach@0
|
121 #define RSHIFT 0
|
|
msach@0
|
122 #define BSHIFT 16
|
|
msach@0
|
123 #endif /* endianess */
|
|
msach@0
|
124 #define GSHIFT 8 /* this is the same in both byte orders */
|
|
msach@0
|
125
|
|
msach@0
|
126 /* some helpful macros... */
|
|
msach@0
|
127 #define SQ(x) ((x) * (x))
|
|
msach@0
|
128 #define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
msach@0
|
129 #define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
msach@0
|
130 #define DOT(a, b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z)
|
|
msach@0
|
131 #define NORMALIZE(a) do {\
|
|
msach@0
|
132 double len = sqrt(DOT(a, a));\
|
|
msach@0
|
133 (a).x /= len; (a).y /= len; (a).z /= len;\
|
|
msach@0
|
134 } while(0);
|
|
msach@0
|
135
|
|
msach@0
|
136 //SSR Message Types
|
|
msach@0
|
137 #define WORK_START 1
|
|
msach@0
|
138 #define WORK_END 2
|
|
msach@0
|
139
|
|
msach@0
|
140 /* global state */
|
|
msach@0
|
141 int xres = 800;
|
|
msach@0
|
142 int yres = 600;
|
|
msach@0
|
143 int rays_per_pixel = 1;
|
|
msach@0
|
144 double aspect = 1.333333;
|
|
msach@0
|
145 struct sphere *obj_list;
|
|
msach@0
|
146 struct vec3 lights[MAX_LIGHTS];
|
|
msach@0
|
147 int lnum = 0;
|
|
msach@0
|
148 struct camera cam;
|
|
msach@0
|
149
|
|
msach@0
|
150 int thread_num = 1;
|
|
msach@0
|
151 struct procr_data *procrs;
|
|
msach@0
|
152
|
|
msach@0
|
153 volatile int end = 0;
|
|
msach@0
|
154 volatile int start = 0;
|
|
msach@0
|
155 int32 end_mutex, end_cond;
|
|
msach@0
|
156 int32 start_cond, start_mutex;
|
|
msach@0
|
157
|
|
msach@0
|
158 #define NRAN 1024
|
|
msach@0
|
159 #define MASK (NRAN - 1)
|
|
msach@0
|
160 struct vec3 urand[NRAN];
|
|
msach@0
|
161 int irand[NRAN];
|
|
msach@0
|
162
|
|
msach@0
|
163 unsigned long rend_time, start_time;
|
|
msach@0
|
164
|
|
msach@0
|
165 const char *usage = {
|
|
msach@0
|
166 "Usage: c-ray-mt [options]\n"
|
|
msach@0
|
167 " Reads a scene file from stdin, writes the image to stdout, and stats to stderr.\n\n"
|
|
msach@0
|
168 "Options:\n"
|
|
msach@0
|
169 " -t <num> how many threads to use (default: 1)\n"
|
|
msach@0
|
170 " -s WxH where W is the width and H the height of the image\n"
|
|
msach@0
|
171 " -r <rays> shoot <rays> rays per pixel (antialiasing)\n"
|
|
msach@0
|
172 " -i <file> read from <file> instead of stdin\n"
|
|
msach@0
|
173 " -o <file> write to <file> instead of stdout\n"
|
|
msach@0
|
174 " -h this help screen\n\n"
|
|
msach@0
|
175 };
|
|
msach@0
|
176
|
|
msach@0
|
177 char __ProgrammName[] = "c-ray";
|
|
msach@0
|
178 char __DataSet[255];
|
|
msach@0
|
179
|
|
msach@0
|
180
|
|
msach@0
|
181 void raytrace(void *pixels, VirtProcr *Vprocr);
|
|
msach@0
|
182
|
|
msach@0
|
183 int main(int argc, char **argv) {
|
|
msach@0
|
184 int i;
|
|
msach@0
|
185 uint32_t *pixels;
|
|
msach@0
|
186 FILE *infile = stdin, *outfile = stdout;
|
|
msach@0
|
187
|
|
msach@0
|
188 for(i=1; i<argc; i++) {
|
|
msach@0
|
189 if(argv[i][0] == '-' && argv[i][2] == 0) {
|
|
msach@0
|
190 char *sep;
|
|
msach@0
|
191 switch(argv[i][1]) {
|
|
msach@0
|
192 case 't':
|
|
msach@0
|
193 if(!isdigit(argv[++i][0])) {
|
|
msach@0
|
194 fprintf(stderr, "-t mus be followed by the number of worker threads to spawn\n");
|
|
msach@0
|
195 return EXIT_FAILURE;
|
|
msach@0
|
196 }
|
|
msach@0
|
197 thread_num = atoi(argv[i]);
|
|
msach@0
|
198 if(!thread_num) {
|
|
msach@0
|
199 fprintf(stderr, "invalid number of threads specified: %d\n", thread_num);
|
|
msach@0
|
200 return EXIT_FAILURE;
|
|
msach@0
|
201 }
|
|
msach@0
|
202 break;
|
|
msach@0
|
203
|
|
msach@0
|
204 case 's':
|
|
msach@0
|
205 if(!isdigit(argv[++i][0]) || !(sep = strchr(argv[i], 'x')) || !isdigit(*(sep + 1))) {
|
|
msach@0
|
206 fputs("-s must be followed by something like \"640x480\"\n", stderr);
|
|
msach@0
|
207 return EXIT_FAILURE;
|
|
msach@0
|
208 }
|
|
msach@0
|
209 xres = atoi(argv[i]);
|
|
msach@0
|
210 yres = atoi(sep + 1);
|
|
msach@0
|
211 aspect = (double)xres / (double)yres;
|
|
msach@0
|
212 break;
|
|
msach@0
|
213
|
|
msach@0
|
214 case 'i':
|
|
msach@0
|
215 if(!(infile = fopen(argv[++i], "rb"))) {
|
|
msach@0
|
216 fprintf(stderr, "failed to open input file %s: %s\n", argv[i], strerror(errno));
|
|
msach@0
|
217 return EXIT_FAILURE;
|
|
msach@0
|
218 }
|
|
msach@0
|
219 break;
|
|
msach@0
|
220
|
|
msach@0
|
221 case 'o':
|
|
msach@0
|
222 if(!(outfile = fopen(argv[++i], "wb"))) {
|
|
msach@0
|
223 fprintf(stderr, "failed to open output file %s: %s\n", argv[i], strerror(errno));
|
|
msach@0
|
224 return EXIT_FAILURE;
|
|
msach@0
|
225 }
|
|
msach@0
|
226 break;
|
|
msach@0
|
227
|
|
msach@0
|
228 case 'r':
|
|
msach@0
|
229 if(!isdigit(argv[++i][0])) {
|
|
msach@0
|
230 fputs("-r must be followed by a number (rays per pixel)\n", stderr);
|
|
msach@0
|
231 return EXIT_FAILURE;
|
|
msach@0
|
232 }
|
|
msach@0
|
233 rays_per_pixel = atoi(argv[i]);
|
|
msach@0
|
234 break;
|
|
msach@0
|
235
|
|
msach@0
|
236 case 'h':
|
|
msach@0
|
237 fputs(usage, stdout);
|
|
msach@0
|
238 return 0;
|
|
msach@0
|
239
|
|
msach@0
|
240 default:
|
|
msach@0
|
241 fprintf(stderr, "unrecognized argument: %s\n", argv[i]);
|
|
msach@0
|
242 fputs(usage, stderr);
|
|
msach@0
|
243 return EXIT_FAILURE;
|
|
msach@0
|
244 }
|
|
msach@0
|
245 } else {
|
|
msach@0
|
246 fprintf(stderr, "unrecognized argument: %s\n", argv[i]);
|
|
msach@0
|
247 fputs(usage, stderr);
|
|
msach@0
|
248 return EXIT_FAILURE;
|
|
msach@0
|
249 }
|
|
msach@0
|
250 }
|
|
msach@0
|
251
|
|
msach@0
|
252
|
|
msach@0
|
253 if(!(pixels = malloc(xres * yres * sizeof *pixels))) {
|
|
msach@0
|
254 perror("pixel buffer allocation failed");
|
|
msach@0
|
255 return EXIT_FAILURE;
|
|
msach@0
|
256 }
|
|
msach@0
|
257 load_scene(infile);
|
|
msach@0
|
258
|
|
msach@0
|
259 //This is the transition to the VMS runtime
|
|
msach@0
|
260 SSR__create_seed_procr_and_do_work(raytrace, (void*)pixels);
|
|
msach@0
|
261
|
|
msach@0
|
262 /* output statistics to stderr */
|
|
msach@0
|
263 fprintf(stderr, "Rendering took: %lu seconds (%lu milliseconds)\n", rend_time / 1000, rend_time);
|
|
msach@0
|
264
|
|
msach@0
|
265 /* output the image */
|
|
msach@0
|
266 fprintf(outfile, "P6\n%d %d\n255\n", xres, yres);
|
|
msach@0
|
267 for(i=0; i<xres * yres; i++) {
|
|
msach@0
|
268 fputc((pixels[i] >> RSHIFT) & 0xff, outfile);
|
|
msach@0
|
269 fputc((pixels[i] >> GSHIFT) & 0xff, outfile);
|
|
msach@0
|
270 fputc((pixels[i] >> BSHIFT) & 0xff, outfile);
|
|
msach@0
|
271 }
|
|
msach@0
|
272 fflush(outfile);
|
|
msach@0
|
273
|
|
msach@0
|
274 if(infile != stdin) fclose(infile);
|
|
msach@0
|
275 if(outfile != stdout) fclose(outfile);
|
|
msach@0
|
276
|
|
msach@0
|
277 struct sphere *walker = obj_list;
|
|
msach@0
|
278 while(walker) {
|
|
msach@0
|
279 struct sphere *tmp = walker;
|
|
msach@0
|
280 walker = walker->next;
|
|
msach@0
|
281 free(tmp);
|
|
msach@0
|
282 }
|
|
msach@0
|
283 free(pixels);
|
|
msach@0
|
284 return 0;
|
|
msach@0
|
285 }
|
|
msach@0
|
286
|
|
msach@0
|
287 /* this is run after the VMS is set up*/
|
|
msach@0
|
288 void raytrace(void *pixels, VirtProcr *VProc)
|
|
msach@0
|
289 {
|
|
msach@0
|
290 int i;
|
|
msach@0
|
291 double sl, sl_per_procr;
|
|
msach@0
|
292
|
|
msach@0
|
293 /* initialize the random number tables for the jitter */
|
|
msach@0
|
294 for(i=0; i<NRAN; i++) urand[i].x = (double)rand() / RAND_MAX - 0.5;
|
|
msach@0
|
295 for(i=0; i<NRAN; i++) urand[i].y = (double)rand() / RAND_MAX - 0.5;
|
|
msach@0
|
296 for(i=0; i<NRAN; i++) irand[i] = (int)(NRAN * ((double)rand() / RAND_MAX));
|
|
msach@0
|
297
|
|
msach@0
|
298 if(thread_num > yres) {
|
|
msach@0
|
299 fprintf(stderr, "more threads than scanlines specified, reducing number of threads to %d\n", yres);
|
|
msach@0
|
300 thread_num = yres;
|
|
msach@0
|
301 }
|
|
msach@0
|
302
|
|
msach@0
|
303
|
|
msach@0
|
304 if(!(procrs = SSR__malloc_to(thread_num * sizeof(procr_data), VProc))) {
|
|
msach@0
|
305 perror("failed to allocate thread table");
|
|
msach@0
|
306 exit(EXIT_FAILURE);
|
|
msach@0
|
307 }
|
|
msach@0
|
308
|
|
msach@0
|
309 sl = 0.0;
|
|
msach@0
|
310 sl_per_procr = (double)yres / (double)thread_num;
|
|
msach@0
|
311 for(i=0; i<thread_num; i++) {
|
|
msach@0
|
312 procrs[i].sl_start = (int)sl;
|
|
msach@0
|
313 sl += sl_per_procr;
|
|
msach@0
|
314 procrs[i].sl_count = (int)sl - procrs[i].sl_start;
|
|
msach@0
|
315 procrs[i].pixels = (uint32_t*)pixels;
|
|
msach@0
|
316 procrs[i].parentVP = VProc;
|
|
msach@0
|
317
|
|
msach@0
|
318 procrs[i].VP = SSR__create_procr_with((VirtProcrFnPtr)thread_func,
|
|
msach@0
|
319 (void*)(&procrs[i]), VProc);
|
|
msach@0
|
320 }
|
|
msach@0
|
321
|
|
msach@0
|
322 procrs[thread_num - 1].sl_count = yres - procrs[thread_num - 1].sl_start;
|
|
msach@0
|
323
|
|
msach@0
|
324 fprintf(stderr, VER_STR, VER_MAJOR, VER_MINOR);
|
|
msach@0
|
325
|
|
msach@0
|
326 // start worker threads
|
|
msach@0
|
327 //printf("start of worker thread (%d)\n", VProc->procrID);
|
|
msach@0
|
328 start_time = get_msec();
|
|
msach@0
|
329 for(i=0; i<thread_num; i++)
|
|
msach@0
|
330 SSR__send_of_type_to(VProc, NULL, WORK_START, procrs[i].VP);
|
|
msach@0
|
331
|
|
msach@0
|
332 //printf("wait for worker (%d)\n", VProc->procrID);
|
|
msach@0
|
333 for(i=0; i<thread_num; i++)
|
|
msach@0
|
334 SSR__receive_type_to(WORK_END, VProc);
|
|
msach@0
|
335
|
|
msach@0
|
336 rend_time = get_msec() - start_time;
|
|
msach@0
|
337
|
|
msach@0
|
338 SSR__free(procrs,VProc);
|
|
msach@0
|
339 SSR__dissipate_procr(VProc);
|
|
msach@0
|
340 }
|
|
msach@0
|
341
|
|
msach@0
|
342 /* render a frame of xsz/ysz dimensions into the provided framebuffer */
|
|
msach@0
|
343 void render_scanline(int xsz, int ysz, int sl, uint32_t *fb, int samples) {
|
|
msach@0
|
344 int i, s;
|
|
msach@0
|
345 double rcp_samples = 1.0 / (double)samples;
|
|
msach@0
|
346
|
|
msach@0
|
347 for(i=0; i<xsz; i++) {
|
|
msach@0
|
348 double r, g, b;
|
|
msach@0
|
349 r = g = b = 0.0;
|
|
msach@0
|
350
|
|
msach@0
|
351 for(s=0; s<samples; s++) {
|
|
msach@0
|
352 struct vec3 col = trace(get_primary_ray(i, sl, s), 0);
|
|
msach@0
|
353 r += col.x;
|
|
msach@0
|
354 g += col.y;
|
|
msach@0
|
355 b += col.z;
|
|
msach@0
|
356 }
|
|
msach@0
|
357
|
|
msach@0
|
358 r = r * rcp_samples;
|
|
msach@0
|
359 g = g * rcp_samples;
|
|
msach@0
|
360 b = b * rcp_samples;
|
|
msach@0
|
361
|
|
msach@0
|
362 fb[sl * xsz + i] = ((uint32_t)(MIN(r, 1.0) * 255.0) & 0xff) << RSHIFT |
|
|
msach@0
|
363 ((uint32_t)(MIN(g, 1.0) * 255.0) & 0xff) << GSHIFT |
|
|
msach@0
|
364 ((uint32_t)(MIN(b, 1.0) * 255.0) & 0xff) << BSHIFT;
|
|
msach@0
|
365 }
|
|
msach@0
|
366 }
|
|
msach@0
|
367
|
|
msach@0
|
368 /* trace a ray throught the scene recursively (the recursion happens through
|
|
msach@0
|
369 * shade() to calculate reflection rays if necessary).
|
|
msach@0
|
370 */
|
|
msach@0
|
371 struct vec3 trace(struct ray ray, int depth) {
|
|
msach@0
|
372 struct vec3 col;
|
|
msach@0
|
373 struct spoint sp, nearest_sp;
|
|
msach@0
|
374 struct sphere *nearest_obj = 0;
|
|
msach@0
|
375 struct sphere *iter = obj_list->next;
|
|
msach@0
|
376
|
|
msach@0
|
377 /* if we reached the recursion limit, bail out */
|
|
msach@0
|
378 if(depth >= MAX_RAY_DEPTH) {
|
|
msach@0
|
379 col.x = col.y = col.z = 0.0;
|
|
msach@0
|
380 return col;
|
|
msach@0
|
381 }
|
|
msach@0
|
382
|
|
msach@0
|
383 /* find the nearest intersection ... */
|
|
msach@0
|
384 while(iter) {
|
|
msach@0
|
385 if(ray_sphere(iter, ray, &sp)) {
|
|
msach@0
|
386 if(!nearest_obj || sp.dist < nearest_sp.dist) {
|
|
msach@0
|
387 nearest_obj = iter;
|
|
msach@0
|
388 nearest_sp = sp;
|
|
msach@0
|
389 }
|
|
msach@0
|
390 }
|
|
msach@0
|
391 iter = iter->next;
|
|
msach@0
|
392 }
|
|
msach@0
|
393
|
|
msach@0
|
394 /* and perform shading calculations as needed by calling shade() */
|
|
msach@0
|
395 if(nearest_obj) {
|
|
msach@0
|
396 col = shade(nearest_obj, &nearest_sp, depth);
|
|
msach@0
|
397 } else {
|
|
msach@0
|
398 col.x = col.y = col.z = 0.0;
|
|
msach@0
|
399 }
|
|
msach@0
|
400
|
|
msach@0
|
401 return col;
|
|
msach@0
|
402 }
|
|
msach@0
|
403
|
|
msach@0
|
404 /* Calculates direct illumination with the phong reflectance model.
|
|
msach@0
|
405 * Also handles reflections by calling trace again, if necessary.
|
|
msach@0
|
406 */
|
|
msach@0
|
407 struct vec3 shade(struct sphere *obj, struct spoint *sp, int depth) {
|
|
msach@0
|
408 int i;
|
|
msach@0
|
409 struct vec3 col = {0, 0, 0};
|
|
msach@0
|
410
|
|
msach@0
|
411 /* for all lights ... */
|
|
msach@0
|
412 for(i=0; i<lnum; i++) {
|
|
msach@0
|
413 double ispec, idiff;
|
|
msach@0
|
414 struct vec3 ldir;
|
|
msach@0
|
415 struct ray shadow_ray;
|
|
msach@0
|
416 struct sphere *iter = obj_list->next;
|
|
msach@0
|
417 int in_shadow = 0;
|
|
msach@0
|
418
|
|
msach@0
|
419 ldir.x = lights[i].x - sp->pos.x;
|
|
msach@0
|
420 ldir.y = lights[i].y - sp->pos.y;
|
|
msach@0
|
421 ldir.z = lights[i].z - sp->pos.z;
|
|
msach@0
|
422
|
|
msach@0
|
423 shadow_ray.orig = sp->pos;
|
|
msach@0
|
424 shadow_ray.dir = ldir;
|
|
msach@0
|
425
|
|
msach@0
|
426 /* shoot shadow rays to determine if we have a line of sight with the light */
|
|
msach@0
|
427 while(iter) {
|
|
msach@0
|
428 if(ray_sphere(iter, shadow_ray, 0)) {
|
|
msach@0
|
429 in_shadow = 1;
|
|
msach@0
|
430 break;
|
|
msach@0
|
431 }
|
|
msach@0
|
432 iter = iter->next;
|
|
msach@0
|
433 }
|
|
msach@0
|
434
|
|
msach@0
|
435 /* and if we're not in shadow, calculate direct illumination with the phong model. */
|
|
msach@0
|
436 if(!in_shadow) {
|
|
msach@0
|
437 NORMALIZE(ldir);
|
|
msach@0
|
438
|
|
msach@0
|
439 idiff = MAX(DOT(sp->normal, ldir), 0.0);
|
|
msach@0
|
440 ispec = obj->mat.spow > 0.0 ? pow(MAX(DOT(sp->vref, ldir), 0.0), obj->mat.spow) : 0.0;
|
|
msach@0
|
441
|
|
msach@0
|
442 col.x += idiff * obj->mat.col.x + ispec;
|
|
msach@0
|
443 col.y += idiff * obj->mat.col.y + ispec;
|
|
msach@0
|
444 col.z += idiff * obj->mat.col.z + ispec;
|
|
msach@0
|
445 }
|
|
msach@0
|
446 }
|
|
msach@0
|
447
|
|
msach@0
|
448 /* Also, if the object is reflective, spawn a reflection ray, and call trace()
|
|
msach@0
|
449 * to calculate the light arriving from the mirror direction.
|
|
msach@0
|
450 */
|
|
msach@0
|
451 if(obj->mat.refl > 0.0) {
|
|
msach@0
|
452 struct ray ray;
|
|
msach@0
|
453 struct vec3 rcol;
|
|
msach@0
|
454
|
|
msach@0
|
455 ray.orig = sp->pos;
|
|
msach@0
|
456 ray.dir = sp->vref;
|
|
msach@0
|
457 ray.dir.x *= RAY_MAG;
|
|
msach@0
|
458 ray.dir.y *= RAY_MAG;
|
|
msach@0
|
459 ray.dir.z *= RAY_MAG;
|
|
msach@0
|
460
|
|
msach@0
|
461 rcol = trace(ray, depth + 1);
|
|
msach@0
|
462 col.x += rcol.x * obj->mat.refl;
|
|
msach@0
|
463 col.y += rcol.y * obj->mat.refl;
|
|
msach@0
|
464 col.z += rcol.z * obj->mat.refl;
|
|
msach@0
|
465 }
|
|
msach@0
|
466
|
|
msach@0
|
467 return col;
|
|
msach@0
|
468 }
|
|
msach@0
|
469
|
|
msach@0
|
470 /* calculate reflection vector */
|
|
msach@0
|
471 struct vec3 reflect(struct vec3 v, struct vec3 n) {
|
|
msach@0
|
472 struct vec3 res;
|
|
msach@0
|
473 double dot = v.x * n.x + v.y * n.y + v.z * n.z;
|
|
msach@0
|
474 res.x = -(2.0 * dot * n.x - v.x);
|
|
msach@0
|
475 res.y = -(2.0 * dot * n.y - v.y);
|
|
msach@0
|
476 res.z = -(2.0 * dot * n.z - v.z);
|
|
msach@0
|
477 return res;
|
|
msach@0
|
478 }
|
|
msach@0
|
479
|
|
msach@0
|
480 struct vec3 cross_product(struct vec3 v1, struct vec3 v2) {
|
|
msach@0
|
481 struct vec3 res;
|
|
msach@0
|
482 res.x = v1.y * v2.z - v1.z * v2.y;
|
|
msach@0
|
483 res.y = v1.z * v2.x - v1.x * v2.z;
|
|
msach@0
|
484 res.z = v1.x * v2.y - v1.y * v2.x;
|
|
msach@0
|
485 return res;
|
|
msach@0
|
486 }
|
|
msach@0
|
487
|
|
msach@0
|
488 /* determine the primary ray corresponding to the specified pixel (x, y) */
|
|
msach@0
|
489 struct ray get_primary_ray(int x, int y, int sample) {
|
|
msach@0
|
490 struct ray ray;
|
|
msach@0
|
491 float m[3][3];
|
|
msach@0
|
492 struct vec3 i, j = {0, 1, 0}, k, dir, orig, foo;
|
|
msach@0
|
493
|
|
msach@0
|
494 k.x = cam.targ.x - cam.pos.x;
|
|
msach@0
|
495 k.y = cam.targ.y - cam.pos.y;
|
|
msach@0
|
496 k.z = cam.targ.z - cam.pos.z;
|
|
msach@0
|
497 NORMALIZE(k);
|
|
msach@0
|
498
|
|
msach@0
|
499 i = cross_product(j, k);
|
|
msach@0
|
500 j = cross_product(k, i);
|
|
msach@0
|
501 m[0][0] = i.x; m[0][1] = j.x; m[0][2] = k.x;
|
|
msach@0
|
502 m[1][0] = i.y; m[1][1] = j.y; m[1][2] = k.y;
|
|
msach@0
|
503 m[2][0] = i.z; m[2][1] = j.z; m[2][2] = k.z;
|
|
msach@0
|
504
|
|
msach@0
|
505 ray.orig.x = ray.orig.y = ray.orig.z = 0.0;
|
|
msach@0
|
506 ray.dir = get_sample_pos(x, y, sample);
|
|
msach@0
|
507 ray.dir.z = 1.0 / HALF_FOV;
|
|
msach@0
|
508 ray.dir.x *= RAY_MAG;
|
|
msach@0
|
509 ray.dir.y *= RAY_MAG;
|
|
msach@0
|
510 ray.dir.z *= RAY_MAG;
|
|
msach@0
|
511
|
|
msach@0
|
512 dir.x = ray.dir.x + ray.orig.x;
|
|
msach@0
|
513 dir.y = ray.dir.y + ray.orig.y;
|
|
msach@0
|
514 dir.z = ray.dir.z + ray.orig.z;
|
|
msach@0
|
515 foo.x = dir.x * m[0][0] + dir.y * m[0][1] + dir.z * m[0][2];
|
|
msach@0
|
516 foo.y = dir.x * m[1][0] + dir.y * m[1][1] + dir.z * m[1][2];
|
|
msach@0
|
517 foo.z = dir.x * m[2][0] + dir.y * m[2][1] + dir.z * m[2][2];
|
|
msach@0
|
518
|
|
msach@0
|
519 orig.x = ray.orig.x * m[0][0] + ray.orig.y * m[0][1] + ray.orig.z * m[0][2] + cam.pos.x;
|
|
msach@0
|
520 orig.y = ray.orig.x * m[1][0] + ray.orig.y * m[1][1] + ray.orig.z * m[1][2] + cam.pos.y;
|
|
msach@0
|
521 orig.z = ray.orig.x * m[2][0] + ray.orig.y * m[2][1] + ray.orig.z * m[2][2] + cam.pos.z;
|
|
msach@0
|
522
|
|
msach@0
|
523 ray.orig = orig;
|
|
msach@0
|
524 ray.dir.x = foo.x + orig.x;
|
|
msach@0
|
525 ray.dir.y = foo.y + orig.y;
|
|
msach@0
|
526 ray.dir.z = foo.z + orig.z;
|
|
msach@0
|
527
|
|
msach@0
|
528 return ray;
|
|
msach@0
|
529 }
|
|
msach@0
|
530
|
|
msach@0
|
531
|
|
msach@0
|
532 struct vec3 get_sample_pos(int x, int y, int sample) {
|
|
msach@0
|
533 struct vec3 pt;
|
|
msach@0
|
534 static double sf = 0.0;
|
|
msach@0
|
535
|
|
msach@0
|
536 if(sf == 0.0) {
|
|
msach@0
|
537 sf = 1.5 / (double)xres;
|
|
msach@0
|
538 }
|
|
msach@0
|
539
|
|
msach@0
|
540 pt.x = ((double)x / (double)xres) - 0.5;
|
|
msach@0
|
541 pt.y = -(((double)y / (double)yres) - 0.65) / aspect;
|
|
msach@0
|
542
|
|
msach@0
|
543 if(sample) {
|
|
msach@0
|
544 struct vec3 jt = jitter(x, y, sample);
|
|
msach@0
|
545 pt.x += jt.x * sf;
|
|
msach@0
|
546 pt.y += jt.y * sf / aspect;
|
|
msach@0
|
547 }
|
|
msach@0
|
548 return pt;
|
|
msach@0
|
549 }
|
|
msach@0
|
550
|
|
msach@0
|
551 /* jitter function taken from Graphics Gems I. */
|
|
msach@0
|
552 struct vec3 jitter(int x, int y, int s) {
|
|
msach@0
|
553 struct vec3 pt;
|
|
msach@0
|
554 pt.x = urand[(x + (y << 2) + irand[(x + s) & MASK]) & MASK].x;
|
|
msach@0
|
555 pt.y = urand[(y + (x << 2) + irand[(y + s) & MASK]) & MASK].y;
|
|
msach@0
|
556 return pt;
|
|
msach@0
|
557 }
|
|
msach@0
|
558
|
|
msach@0
|
559 /* Calculate ray-sphere intersection, and return {1, 0} to signify hit or no hit.
|
|
msach@0
|
560 * Also the surface point parameters like position, normal, etc are returned through
|
|
msach@0
|
561 * the sp pointer if it is not NULL.
|
|
msach@0
|
562 */
|
|
msach@0
|
563 int ray_sphere(const struct sphere *sph, struct ray ray, struct spoint *sp) {
|
|
msach@0
|
564 double a, b, c, d, sqrt_d, t1, t2;
|
|
msach@0
|
565
|
|
msach@0
|
566 a = SQ(ray.dir.x) + SQ(ray.dir.y) + SQ(ray.dir.z);
|
|
msach@0
|
567 b = 2.0 * ray.dir.x * (ray.orig.x - sph->pos.x) +
|
|
msach@0
|
568 2.0 * ray.dir.y * (ray.orig.y - sph->pos.y) +
|
|
msach@0
|
569 2.0 * ray.dir.z * (ray.orig.z - sph->pos.z);
|
|
msach@0
|
570 c = SQ(sph->pos.x) + SQ(sph->pos.y) + SQ(sph->pos.z) +
|
|
msach@0
|
571 SQ(ray.orig.x) + SQ(ray.orig.y) + SQ(ray.orig.z) +
|
|
msach@0
|
572 2.0 * (-sph->pos.x * ray.orig.x - sph->pos.y * ray.orig.y - sph->pos.z * ray.orig.z) - SQ(sph->rad);
|
|
msach@0
|
573
|
|
msach@0
|
574 if((d = SQ(b) - 4.0 * a * c) < 0.0) return 0;
|
|
msach@0
|
575
|
|
msach@0
|
576 sqrt_d = sqrt(d);
|
|
msach@0
|
577 t1 = (-b + sqrt_d) / (2.0 * a);
|
|
msach@0
|
578 t2 = (-b - sqrt_d) / (2.0 * a);
|
|
msach@0
|
579
|
|
msach@0
|
580 if((t1 < ERR_MARGIN && t2 < ERR_MARGIN) || (t1 > 1.0 && t2 > 1.0)) return 0;
|
|
msach@0
|
581
|
|
msach@0
|
582 if(sp) {
|
|
msach@0
|
583 if(t1 < ERR_MARGIN) t1 = t2;
|
|
msach@0
|
584 if(t2 < ERR_MARGIN) t2 = t1;
|
|
msach@0
|
585 sp->dist = t1 < t2 ? t1 : t2;
|
|
msach@0
|
586
|
|
msach@0
|
587 sp->pos.x = ray.orig.x + ray.dir.x * sp->dist;
|
|
msach@0
|
588 sp->pos.y = ray.orig.y + ray.dir.y * sp->dist;
|
|
msach@0
|
589 sp->pos.z = ray.orig.z + ray.dir.z * sp->dist;
|
|
msach@0
|
590
|
|
msach@0
|
591 sp->normal.x = (sp->pos.x - sph->pos.x) / sph->rad;
|
|
msach@0
|
592 sp->normal.y = (sp->pos.y - sph->pos.y) / sph->rad;
|
|
msach@0
|
593 sp->normal.z = (sp->pos.z - sph->pos.z) / sph->rad;
|
|
msach@0
|
594
|
|
msach@0
|
595 sp->vref = reflect(ray.dir, sp->normal);
|
|
msach@0
|
596 NORMALIZE(sp->vref);
|
|
msach@0
|
597 }
|
|
msach@0
|
598 return 1;
|
|
msach@0
|
599 }
|
|
msach@0
|
600
|
|
msach@0
|
601 /* Load the scene from an extremely simple scene description file */
|
|
msach@0
|
602 #define DELIM " \t\n"
|
|
msach@0
|
603 void load_scene(FILE *fp) {
|
|
msach@0
|
604 char line[256], *ptr, type;
|
|
msach@0
|
605
|
|
msach@0
|
606 obj_list = malloc(sizeof(struct sphere));
|
|
msach@0
|
607 obj_list->next = 0;
|
|
msach@0
|
608
|
|
msach@0
|
609 while((ptr = fgets(line, 256, fp))) {
|
|
msach@0
|
610 int i;
|
|
msach@0
|
611 struct vec3 pos, col;
|
|
msach@0
|
612 double rad, spow, refl;
|
|
msach@0
|
613
|
|
msach@0
|
614 while(*ptr == ' ' || *ptr == '\t') ptr++;
|
|
msach@0
|
615 if(*ptr == '#' || *ptr == '\n') continue;
|
|
msach@0
|
616
|
|
msach@0
|
617 if(!(ptr = strtok(line, DELIM))) continue;
|
|
msach@0
|
618 type = *ptr;
|
|
msach@0
|
619
|
|
msach@0
|
620 for(i=0; i<3; i++) {
|
|
msach@0
|
621 if(!(ptr = strtok(0, DELIM))) break;
|
|
msach@0
|
622 *((double*)&pos.x + i) = atof(ptr);
|
|
msach@0
|
623 }
|
|
msach@0
|
624
|
|
msach@0
|
625 if(type == 'l') {
|
|
msach@0
|
626 lights[lnum++] = pos;
|
|
msach@0
|
627 continue;
|
|
msach@0
|
628 }
|
|
msach@0
|
629
|
|
msach@0
|
630 if(!(ptr = strtok(0, DELIM))) continue;
|
|
msach@0
|
631 rad = atof(ptr);
|
|
msach@0
|
632
|
|
msach@0
|
633 for(i=0; i<3; i++) {
|
|
msach@0
|
634 if(!(ptr = strtok(0, DELIM))) break;
|
|
msach@0
|
635 *((double*)&col.x + i) = atof(ptr);
|
|
msach@0
|
636 }
|
|
msach@0
|
637
|
|
msach@0
|
638 if(type == 'c') {
|
|
msach@0
|
639 cam.pos = pos;
|
|
msach@0
|
640 cam.targ = col;
|
|
msach@0
|
641 cam.fov = rad;
|
|
msach@0
|
642 continue;
|
|
msach@0
|
643 }
|
|
msach@0
|
644
|
|
msach@0
|
645 if(!(ptr = strtok(0, DELIM))) continue;
|
|
msach@0
|
646 spow = atof(ptr);
|
|
msach@0
|
647
|
|
msach@0
|
648 if(!(ptr = strtok(0, DELIM))) continue;
|
|
msach@0
|
649 refl = atof(ptr);
|
|
msach@0
|
650
|
|
msach@0
|
651 if(type == 's') {
|
|
msach@0
|
652 struct sphere *sph = malloc(sizeof *sph);
|
|
msach@0
|
653 sph->next = obj_list->next;
|
|
msach@0
|
654 obj_list->next = sph;
|
|
msach@0
|
655
|
|
msach@0
|
656 sph->pos = pos;
|
|
msach@0
|
657 sph->rad = rad;
|
|
msach@0
|
658 sph->mat.col = col;
|
|
msach@0
|
659 sph->mat.spow = spow;
|
|
msach@0
|
660 sph->mat.refl = refl;
|
|
msach@0
|
661 } else {
|
|
msach@0
|
662 fprintf(stderr, "unknown type: %c\n", type);
|
|
msach@0
|
663 }
|
|
msach@0
|
664 }
|
|
msach@0
|
665 }
|
|
msach@0
|
666
|
|
msach@0
|
667
|
|
msach@0
|
668 /* provide a millisecond-resolution timer for each system */
|
|
msach@0
|
669 #if defined(unix) || defined(__unix__)
|
|
msach@0
|
670 #include <time.h>
|
|
msach@0
|
671 #include <sys/time.h>
|
|
msach@0
|
672 unsigned long get_msec(void) {
|
|
msach@0
|
673 static struct timeval timeval, first_timeval;
|
|
msach@0
|
674
|
|
msach@0
|
675 gettimeofday(&timeval, 0);
|
|
msach@0
|
676 if(first_timeval.tv_sec == 0) {
|
|
msach@0
|
677 first_timeval = timeval;
|
|
msach@0
|
678 return 0;
|
|
msach@0
|
679 }
|
|
msach@0
|
680 return (timeval.tv_sec - first_timeval.tv_sec) * 1000 + (timeval.tv_usec - first_timeval.tv_usec) / 1000;
|
|
msach@0
|
681 }
|
|
msach@0
|
682 #elif defined(__WIN32__) || defined(WIN32)
|
|
msach@0
|
683 #include <windows.h>
|
|
msach@0
|
684 unsigned long get_msec(void) {
|
|
msach@0
|
685 return GetTickCount();
|
|
msach@0
|
686 }
|
|
msach@0
|
687 #else
|
|
msach@0
|
688 #error "I don't know how to measure time on your platform"
|
|
msach@0
|
689 #endif
|
|
msach@0
|
690
|
|
msach@0
|
691 void thread_func(void *tdata, VirtProcr *VProc) {
|
|
msach@0
|
692 int i;
|
|
msach@0
|
693 procr_data *td = (procr_data*)tdata;
|
|
msach@0
|
694
|
|
msach@0
|
695 SSR__receive_type_to(WORK_START, VProc);
|
|
msach@0
|
696
|
|
msach@0
|
697 for(i=0; i<td->sl_count; i++) {
|
|
msach@0
|
698 render_scanline(xres, yres, i + td->sl_start, td->pixels, rays_per_pixel);
|
|
msach@0
|
699 }
|
|
msach@0
|
700
|
|
msach@0
|
701 SSR__send_of_type_to(VProc, NULL, WORK_END, td->parentVP);
|
|
msach@0
|
702
|
|
msach@0
|
703 SSR__dissipate_procr(VProc);
|
|
msach@0
|
704 }
|