This is raytrace.c in view mode; [Download] [Up]
/* * raytrace.c * * Copyright (C) 1989, 1991, Craig E. Kolb * All rights reserved. * * This software may be freely copied, modified, and redistributed * provided that this copyright notice is preserved on all copies. * * You may not distribute this software, in whole or in part, as part of * any commercial product without the express consent of the authors. * * There is no warranty or other guarantee of fitness of this software * for any purpose. It is provided solely "as is". * * $Id$ * * $Log$ */ /* * This module could use some work. In particular, a better antialiasing * scheme would be nice. */ #include "rayshade.h" #include "libsurf/atmosphere.h" #include "libsurf/surface.h" #include "libcommon/sampling.h" #include "options.h" #include "stats.h" #include "raytrace.h" #include "viewing.h" static Pixel *pixel_buf[2]; /* Point buffer, background color */ static Pixel *out_buf; /* Output pixel buffer */ static pixel_square *SquareStack; static Float Minsquare; static int *SampleNumbers; static void distributed_trace(), trace_jit_line(), trace_jit_pixel(), adaptive_trace(), trace_line(), subdivide_line(), subdivide_square(), trace_point(); static int pixel_ok(); static Ray TopRay; /* Top-level ray. */ /* * "Dither matrices" used to encode the 'number' of a ray that samples a * particular portion of a pixel. Hand-coding is ugly, but... */ static int OneSample[1] = {0}; static int TwoSamples[4] = {0, 2, 3, 1}; static int ThreeSamples[9] = {0, 2, 7, 6, 5, 1, 3, 8, 4}; static int FourSamples[16] = { 0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5}; static int FiveSamples[25] = { 0, 8, 23, 17, 2, 19, 12, 4, 20, 15, 3, 21, 16, 9, 6, 14, 10, 24, 1, 13, 22, 7, 18, 11, 5}; static int SixSamples[36] = { 6, 32, 3, 34, 35, 1, 7, 11, 27, 28, 8, 30, 24, 14, 16, 15, 23, 19, 13, 20, 22, 21, 17, 18, 25, 29, 10, 9, 26, 12, 36, 5, 33, 4, 2, 31}; static int SevenSamples[49] = {22, 47, 16, 41, 10, 35, 4, 5, 23, 48, 17, 42, 11, 29, 30, 6, 24, 49, 18, 36, 12, 13, 31, 7, 25, 43, 19, 37, 38, 14, 32, 1, 26, 44, 20, 21, 39, 8, 33, 2, 27, 45, 46, 15, 40, 9, 34, 3, 28}; static int EightSamples[64] = { 8, 58, 59, 5, 4, 62, 63, 1, 49, 15, 14, 52, 53, 11, 10, 56, 41, 23, 22, 44, 45, 19, 18, 48, 32, 34, 35, 29, 28, 38, 39, 25, 40, 26, 27, 37, 36, 30, 31, 33, 17, 47, 46, 20, 21, 43, 42, 24, 9, 55, 54, 12, 13, 51, 50, 16, 64, 2, 3, 61, 60, 6, 7, 57}; void raytrace() { /* * The top-level ray TopRay always has as its origin the * eye position and as its medium NULL, indicating that it * is passing through a medium with index of refraction * equal to DefIndex. */ TopRay.pos = Camera.pos; TopRay.media = (Medium *)0; TopRay.depth = 0; out_buf = (Pixel *)Malloc(Screen.xsize * sizeof(Pixel)); if (Options.jittered) distributed_trace(); else adaptive_trace(); } /* * Raytrace an image using "distributed" raytracing. */ static void distributed_trace() { int y; Float usertime, systime, lasttime; switch (Options.jit_samples) { case 1: SampleNumbers = OneSample; break; case 2: SampleNumbers = TwoSamples; break; case 3: SampleNumbers = ThreeSamples; break; case 4: SampleNumbers = FourSamples; break; case 5: SampleNumbers = FiveSamples; break; case 6: SampleNumbers = SixSamples; break; case 7: SampleNumbers = SevenSamples; break; case 8: SampleNumbers = EightSamples; break; default: RLerror(RL_PANIC, "Sorry, %d rays/pixel not supported.\n", Options.jit_samples*Options.jit_samples); } /* * Trace each scanline, writing results to output file. */ lasttime = 0; for (y = Screen.miny; y <= Screen.maxy; y++) { trace_jit_line(y, out_buf); PictureWriteLine(out_buf); if (y % Options.report_freq == 0) { fprintf(Stats.fstats,"Finished line %d (%lu rays",y, Stats.EyeRays); if (Options.verbose) { /* * Report total CPU and split times. */ get_cpu_time(&usertime, &systime); fprintf(Stats.fstats,", %2.2f sec,", usertime+systime); fprintf(Stats.fstats," %2.2f split", usertime+systime-lasttime); lasttime = usertime+systime; } fprintf(Stats.fstats,")\n"); (void)fflush(Stats.fstats); } } } static void trace_jit_line(line, buf) int line; Pixel *buf; { register int x; for (x = Screen.minx; x <= Screen.maxx; x++) trace_jit_pixel(x, line, buf++); } static void trace_jit_pixel(xp, yp, color) int xp, yp; Pixel *color; { Pixel tmp; Float x, y, xpos, ypos; int i, j, index; ypos = (Float)yp - 0.5; color->r = color->g = color->b = color->alpha = 0.; index = 0; for (i = 0; i < Options.jit_samples; i++, ypos += Sampling.spacing) { xpos = (Float)xp - 0.5; for (j=0; j < Options.jit_samples; j++, xpos += Sampling.spacing) { x = xpos + nrand() * Sampling.spacing; y = ypos + nrand() * Sampling.spacing; trace_point(x, y, &tmp, SampleNumbers[index++]); color->r += tmp.r; color->g += tmp.g; color->b += tmp.b; color->alpha += tmp.alpha; } } color->r *= Sampling.weight; color->g *= Sampling.weight; color->b *= Sampling.weight; color->alpha *= Sampling.weight; } /* * Raytrace an image using adaptive supersampling to perform antialising. */ static void adaptive_trace() { int line; Float usertime, systime, lasttime; pixel_buf[0] = (Pixel *)Malloc(sizeof(Pixel) * (unsigned)(Screen.xsize + 1)); pixel_buf[1] = (Pixel *)Malloc(sizeof(Pixel) * (unsigned)(Screen.xsize + 1)); /* * Minimum pixel square size. */ Minsquare = 1./pow(2.,(Float)Options.pixel_div); /* * At any time, there can be a maximum of 3 * pixel_div + 1 * pixel squares on the stack. */ SquareStack = (pixel_square *)Malloc((unsigned)(3*Options.pixel_div+1)* sizeof(pixel_square)); /* * A pixel is treated as a square through whose corners rays * are traced. If the color values at the corners are * "too different" (as measured by pixel_ok()), the square is * divided into four squares (tracing 5 additional rays) * and the process is repeated on the four new squares. * The color assigned to a square is the average of the 4 corners. * Note that this means that adjacent super-sampled pixels * cause the same ray to be traced more than once. * This scheme is adequate but far from wonderful. */ line = Screen.miny; trace_line(line, &pixel_buf[line & 01][0]); lasttime = 0; for(line++; line <= Screen.maxy + 1; line++) { trace_line(line, &pixel_buf[line & 01][0]); subdivide_line(line -1, pixel_buf[line & 01], pixel_buf[(line+1) & 01], out_buf); PictureWriteLine(out_buf); if (line % Options.report_freq == 0) { fprintf(Stats.fstats,"Finished line %d (%lu rays",line, Stats.EyeRays); if (Options.verbose) { /* * Report total CPU and split times. */ get_cpu_time(&usertime, &systime); fprintf(Stats.fstats,", %2.2f sec,", usertime+systime); fprintf(Stats.fstats," %2.2f split", usertime+systime-lasttime); lasttime = usertime+systime; } fprintf(Stats.fstats,")\n"); (void)fflush(Stats.fstats); } } } /* * Trace a line of sample points along "line". */ static void trace_line(line, buf) int line; Pixel *buf; { register int x; Float y, xoff; /* * Line # 'line' has a 'y' value of line - 0.5. */ y = line - 0.5; /* * Since we're samping pixel corners, we need to subtract * 0.5 from the value of each x. */ xoff = (Float)Screen.minx - 0.5; /* * We need to trace xsize + 1 rays. */ for (x = 0; x <= Screen.xsize; x++) trace_point((Float)x + xoff, y, buf + x, -1); } /* * Given the two arrays of sample points that define the upper and * lower edges of all the pixel squares in line "y," push each * square in turn on the pixelsquare stack and determine a color value * for the pixel by calling subdivide_square(). */ static void subdivide_line(y, upper, lower, buf) int y; Pixel *upper, *lower, *buf; { register int x; /* * Upper is the array of sample values that define the "upper" * part (corners) of this row, while lower are the "lower" corners. * For the next (lower) row, the current "upper" becomes "lower". */ for(x = 0; x < Screen.xsize; x++) { SquareStack[0].x = (Float)(x + Screen.minx); SquareStack[0].y = (Float)y; SquareStack[0].size = 1.0; SquareStack[0].ul = upper[x]; SquareStack[0].ur = upper[x+1]; SquareStack[0].ll = lower[x]; SquareStack[0].lr = lower[x+1]; subdivide_square(&buf[x]); } } /* * Bleck, but it saves us a function call and keeps the code much cleaner. */ #define push_square(u,v,s,a,b,c,d) {\ Stackp->x = u; \ Stackp->y = v; \ Stackp->size = s; \ Stackp->ul = a; \ Stackp->ur = b; \ Stackp->ll = c; \ Stackp->lr = d; \ Stackp++;} /* * Subdivide a pixel square. */ static void subdivide_square(color) Pixel *color; { register pixel_square *Stackp; Float x, y, size, halfsize; Float avfact, xdelta, ydelta; Pixel ul, ur, ll, lr, u, d, l, r, c; color->r = color->g = color->b = color->alpha = 0.; Stackp = SquareStack + 1; do { Stackp--; size = Stackp->size; ul = Stackp->ul; ur = Stackp->ur; ll = Stackp->ll; lr = Stackp->lr; if (size <= Minsquare || pixel_ok(&ul,&ur,&ll,&lr)) { /* * The square is either the smallest allowed, or * the four corners of the square are similar. * Average the four corners (weighted by the * size of the square) to get this square's * contribution to the whole pixel's color. */ avfact = (size * size) * 0.25; color->r += (ul.r + ur.r + ll.r + lr.r) * avfact; color->g += (ul.g + ur.g + ll.g + lr.g) * avfact; color->b += (ul.b + ur.b + ll.b + lr.b) * avfact; color->alpha += (ul.alpha+ur.alpha+ll.alpha+lr.alpha) * avfact; continue; } /* * Subdivide into four squares -- trace 5 additional * rays and push the appropriate squares on the pixelsquare * stack. */ x = Stackp->x; y = Stackp->y; halfsize = size * 0.5; xdelta = x + halfsize; ydelta = y + halfsize; trace_point(xdelta, y, &u, -1); trace_point(x, ydelta, &l, -1); trace_point(xdelta, ydelta, &c, -1); trace_point(x + size, ydelta, &r, -1); trace_point(xdelta, y + size, &d, -1); if(size == 1.) Stats.SuperSampled++; push_square(x, y, halfsize, ul, u, l, c); push_square(xdelta, y, halfsize, u, ur, c, r); push_square(x, ydelta, halfsize, l, c, ll, d); push_square(xdelta, ydelta, halfsize, c, r, d, lr); } while (Stackp != SquareStack); } /* * Trace a ray through x, y on the screen, placing the result in "color." */ static void trace_point(x, y, color, sample) Float x, y; /* Screen position to sample */ Pixel *color; /* resulting color */ int sample; /* sample number, < 0 indicates single sample */ { Float dist; HitList hitlist; Color ctmp; extern void focus_blur_ray(), ShadeRay(); /* * Calculate ray direction. */ Stats.EyeRays++; TopRay.dir.x = Screen.firstray.x + x*Screen.scrnx.x + y*Screen.scrny.x; TopRay.dir.y = Screen.firstray.y + x*Screen.scrnx.y + y*Screen.scrny.y; TopRay.dir.z = Screen.firstray.z + x*Screen.scrnx.z + y*Screen.scrny.z; (void)VecNormalize(&TopRay.dir); TopRay.sample = sample; if (Camera.aperture > 0.0) { /* * If the aperture is open, adjust the initial ray * to account for depth of field. */ focus_blur_ray(&TopRay); } /* * Do the actual ray trace. */ dist = FAR_AWAY; hitlist.nodes = 0; (void)TraceRay(&TopRay, &hitlist, EPSILON, &dist); ShadeRay(&hitlist, &TopRay, dist, &Screen.background, &ctmp, 1.0); color->r = ctmp.r; color->g = ctmp.g; color->b = ctmp.b; if (hitlist.nodes != 0) { color->alpha = 1.; } else { color->alpha = 0.; } } /* * Return TRUE if this pixel is okay and doesn't need to be supersampled, * FALSE otherwise. */ static int pixel_ok(w,x,y,z) Pixel *w, *x, *y, *z; { Float rmax, rmin, gmax, gmin, bmax, bmin; Float rsum, gsum, bsum; /* * Find min & max R, G, & B. */ rmax = max(w->r, max(x->r, max(y->r, z->r))); rmin = min(w->r, min(x->r, min(y->r, z->r))); gmax = max(w->g, max(x->g, max(y->g, z->g))); gmin = min(w->g, min(x->g, min(y->g, z->g))); bmax = max(w->b, max(x->b, max(y->b, z->b))); bmin = min(w->b, min(x->b, min(y->b, z->b))); /* * Contrast is defined as (Max - Min) / (Max + Min) for each * of RG&B. If any of these values is greater than the maximum * allowed, we have to supersample. */ rsum = rmax + rmin; gsum = gmax + gmin; bsum = bmax + bmin; if ((rsum == 0. || (rmax - rmin) / rsum < Options.contrast.r) && (gsum == 0. || (bmax - bmin) / gsum < Options.contrast.g) && (bsum == 0. || (gmax - gmin) / bsum < Options.contrast.b)) return TRUE; return FALSE; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.