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