home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
OSK
/
GRAPHICS
/
rayshade.lzh
/
raytrace.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-05-08
|
20KB
|
757 lines
/*
* raytrace.c
*
* Copyright (C) 1989, Craig E. Kolb
*
* This software may be freely copied, modified, and redistributed,
* provided that this copyright notice is preserved on all copies.
*
* There is no warranty or other guarantee of fitness for this software,
* it is provided solely . Bug reports or fixes may be sent
* to the author, who may or may not act on them as he desires.
*
* You may not include this software in a program or other software product
* without supplying the source, or without informing the end-user that the
* source is available for no extra charge.
*
* If you modify this software, you should include a notice giving the
* name of the person performing the modification, the date of modification,
* and the reason for such modification.
*
* $Id: raytrace.c,v 3.0.1.6 90/02/12 13:29:09 craig Exp $
*
* $Log: raytrace.c,v $
* Revision 3.0.1.6 90/02/12 13:29:09 craig
* patch4: Changes to reflect new variable names in linda code.
*
* Revision 3.0.1.5 89/12/07 22:55:25 craig
* patch2: Renamed utime and stime to avoid name clashes.
*
* Revision 3.0.1.4 89/12/02 16:41:46 craig
* patch2: Added ReportFreq, reporting of total CPU & split times.
*
* Revision 3.0.1.3 89/12/02 14:42:37 craig
* patch2: Added call to focus_blur_ray() to support depth of field.
*
* Revision 3.0.1.2 89/11/16 20:35:30 craig
* patch1: ShadeRay is now called on background rays.
*
* Revision 3.0.1.1 89/11/16 18:27:38 craig
* patch1: Workers now report statistics to supervisor.
* patch1: Linda syntax is more up-to-date.
*
* Revision 3.0 89/10/27 02:06:02 craig
* Baseline for first official release.
*
*/
/*
* This module could use some work. In particular, a better antialiasing
* scheme would be nice.
*/
#ifdef LINDA
/*
* If you're using linda, be sure to *move* this file to "raytrace.cl"
* before compiling.
*/
#include "linda.h"
#endif
#include <math.h>
#include <stdio.h>
#include "typedefs.h"
#include "constants.h"
#include "funcdefs.h"
#include "raytrace.h"
Color *pixel_buf[2], background; /* Point buffer, background color */
Color *out_buf; /* Output pixel buffer */
int pixel_div = UNSET; /* max times to subdivide pixel */
int JitSamples = UNSET; /* sqrt of # jittered samples/pixel */
double JitterWeight; /* 1. / (Total Samples Taken) */
int Jittered; /* Use distributed raytracing */
int *SampleNumbers;
int SampleNumber;
int StartLine = UNSET;
int ReportFreq; /* Frequency of status report */
double RedContrast = UNSET, GreenContrast = UNSET, BlueContrast = UNSET;
double SampleSpacing;
#ifdef LINDA
extern int Workers;
int adapt_worker(), dist_worker();
#endif
extern int Xres, Yres;
extern Vector eyep, firstray;
Ray TopRay; /* Top-level ray. */
#ifdef LINDA
real_main(argc, argv)
int argc;
char **argv;
{
/*
* Linda wants all routines, including lmain(), to be in a single
* file. So, we have to perform this bit of silliness.
*/
return rayshade_main(argc, argv);
}
#endif
raytrace()
{
#ifdef LINDA
extern unsigned long primtests[], primhits[];
extern double Utime, Stime;
extern unsigned long EyeRays, ShadowRays, ReflectRays, RefractRays,
CacheWorked, CacheFailed, ShadowHits, SuperSampled,
BVTests, HitRays;
unsigned long eyerays, shadowrays, reflectrays, refractrays,
cacheworked, cachefailed, shadowhits, supersampled,
bvtests, hitrays;
unsigned long primtmp[PRIMTYPES], hittmp[PRIMTYPES];
double utmp, stmp;
int i, j;
extern FILE *fstats;
#endif
/*
* 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 = eyep;
TopRay.media = (SurfaceList *)0;
TopRay.shadow = FALSE;
out_buf = (Color *)Malloc(Xres * sizeof(Color));
if (Jittered)
distributed_trace();
else
adaptive_trace();
#ifdef LINDA
/*
* In statistics & workers.
*/
for (i = 0; i < Workers; i++) {
in ("statistics", ? eyerays, ? shadowrays, ? reflectrays,
? refractrays, ? cacheworked, ? cachefailed,
? shadowhits, ? supersampled, ? bvtests, ? hitrays);
EyeRays += eyerays;
ShadowRays += shadowrays;
ReflectRays += reflectrays;
RefractRays += refractrays;
CacheWorked += cacheworked;
ShadowHits += shadowhits;
SuperSampled += supersampled;
BVTests += bvtests;
HitRays += hitrays;
in ("counts", ? primtmp, ? hittmp);
for (j = 0; j < PRIMTYPES; j++) {
primtests[j] += primtmp[j];
primhits[j] += hittmp[j];
}
in ("timing", ? utmp, ? stmp);
Utime += utmp / (double)Workers;
Stime += stmp / (double)Workers;
in("worker", ? int);
}
#endif
}
/*
* Raytrace an image using "distributed" raytracing.
*/
distributed_trace()
{
register int y;
extern FILE *fstats;
extern unsigned long EyeRays;
#ifdef LINDA
extern int VerboseWorker;
#else
double usertime, systime, lasttime;
extern int Verbose;
#endif
switch (JitSamples) {
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;
default:
fprintf(stderr,"Sorry, %d rays/pixel not supported.\n",
JitSamples*JitSamples);
exit(2);
}
JitterWeight= 1. / (JitSamples * JitSamples);
SampleSpacing = 1. / JitSamples;
#ifdef LINDA
/*
* Linda strategy:
* There is one tuple, named "scaninfo", which holds the number of
* the next line to be worked on. A worker ins the scaninfo tuple,
* notes its value, and increments it before outing it again.
* The supervisor ins finished scanlines in order and writes them to
* the output file.
*/
fprintf(fstats,"Using %d workers.\n",Workers);
fflush(fstats);
out("scaninfo", StartLine);
for (y = 0; y < Workers; y++)
eval("worker", dist_worker());
for (y = StartLine; y >= 0 ; y--) {
in("result", y, ? out_buf:);
if (VerboseWorker)
fprintf(stderr,"Supervisor: inned %d\n",y);
if (y % ReportFreq == 0)
fprintf(fstats, "Finished line %d.\n",y);
fflush(fstats);
outline(out_buf);
}
#else
/*
* Trace each scanline, writing results to output file.
*/
lasttime = 0;
for (y = StartLine; y >= 0; y--) {
trace_jit_line(y, out_buf);
outline(out_buf);
if (y % ReportFreq == 0) {
fprintf(fstats,"Finished line %d (%ld rays",y,
EyeRays);
if (Verbose) {
/*
* Report total CPU and split times.
*/
get_cpu_time(&usertime, &systime);
fprintf(fstats,", %2.2lf sec,",
usertime+systime);
fprintf(fstats," %2.2lf split",
usertime+systime-lasttime);
lasttime = usertime+systime;
}
fprintf(fstats,")\n");
fflush(fstats);
}
}
#endif
}
trace_jit_line(line, buf)
int line;
Color *buf;
{
register int x;
for (x = 0; x < Xres; x++)
trace_jit_pixel(x, line, buf++);
}
trace_jit_pixel(xp, yp, color)
int xp, yp;
Color *color;
{
Color tmp;
double x, y, xpos, ypos;
int i, j, index;
ypos = (double)yp - 0.5;
color->r = color->g = color->b = 0.;
index = 0;
for (i = 0; i < JitSamples; i++, ypos += SampleSpacing) {
xpos = (double)xp - 0.5;
for (j = 0; j < JitSamples; j++, xpos += SampleSpacing) {
x = xpos + nrand() * SampleSpacing;
y = ypos + nrand() * SampleSpacing;
SampleNumber = SampleNumbers[index++];
trace_point(x, y, &tmp);
color->r += tmp.r;
color->g += tmp.g;
color->b += tmp.b;
}
}
ScaleColor(JitterWeight, *color, color);
}
/*
* Raytrace an image using adaptive supersampling to perform antialising.
*/
adaptive_trace()
{
register int line;
extern unsigned long EyeRays;
extern FILE *fstats;
#ifdef LINDA
extern int VerboseWorker;
#else
double usertime, systime, lasttime;
extern int Verbose;
#endif
/*
* In the adaptive supersampling case, Jitter, JitterWeight,
* and SampleSpacing refer are used in sampling extended
* light sources. JitterWeight has a -4 in the denominator
* due to the fact that we don't sample the corners of extended
* sources when performing adaptive supersampling.
*/
JitterWeight = 1. / (JitSamples * JitSamples - 4);
SampleSpacing = 1. / JitSamples;
pixel_buf[0] = (Color *)Malloc(sizeof(Color) *
(unsigned)(Xres + 1));
pixel_buf[1] = (Color *)Malloc(sizeof(Color) *
(unsigned)(Xres + 1));
/*
* Minimum pixel square size.
*/
Minsquare = 1./pow(2.,(double)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 * 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.
*/
#ifdef LINDA
/*
* This is a bit more complicated than 'jittered' sampling,
* as workers must know about other scanlines.
*
* The basic idea is to have a "scanline" tuple which
* holds the last pixline handed out and the last scanline
* completed. This should be improved by keeping a tuple
* containing the last completed scanline/pixline, and not
* just the last ones assigned. (The problem being that a
* worker can try to in a pixline while another worker
* is still working on it.)
*/
fprintf(fstats,"Using %d workers.\n",Workers);
fflush(fstats);
out("scaninfo", StartLine+1, StartLine+2);
for (line = 0; line < Workers; line++)
eval("worker", adapt_worker());
/*
* in() the scanlines in order, writing to output file as we go.
* This loop can easily be modified to make the supervisor
* do some work, too.
*/
for (line = StartLine; line >= 0;) {
if (!adapt_job(TRUE))
sleep(5);
while (inp("result", line, ? out_buf:)) {
if (VerboseWorker)
fprintf(stderr,"Supervisor: inned %d\n",line);
if (line % ReportFreq == 0) {
fprintf(fstats, "Finished line %d.\n",line);
fflush(fstats);
}
outline(out_buf);
if (--line < 0)
break;
}
}
#else
line = StartLine + 1;
trace_line(line, &pixel_buf[line & 01][0]);
lasttime = 0;
/*
* Work bottom-to-top, as that's the way Utah-raster wants to
* operate.
*/
for(line--;line >= 0;line--) {
trace_line(line, &pixel_buf[line & 01][0]);
subdivide_line(line, pixel_buf[line & 01],
pixel_buf[(line+1) & 01],
out_buf);
outline(out_buf);
if(line % ReportFreq == 0) {
fprintf(fstats,"Finished line %d (%ld rays",line,
EyeRays);
if (Verbose) {
/*
* Report total CPU and split times.
*/
get_cpu_time(&usertime, &systime);
fprintf(fstats,", %2.2lf sec,",
usertime+systime);
fprintf(fstats," %2.2lf split",
usertime+systime-lasttime);
lasttime = usertime+systime;
}
fprintf(fstats,")\n");
fflush(fstats);
}
}
#endif
}
/*
* Trace a line of sample points along "line".
*/
trace_line(line, buf)
int line;
Color *buf;
{
register int x;
/*
* We need to trace Xres + 1 rays.
*/
for(x = 0; x <= Xres;x++)
trace_point((double)x, (double)line, buf + x);
}
/*
* Given the two arrays of sample points which 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().
*/
subdivide_line(y, upper, lower, buf)
int y;
Color *upper, *lower, *buf;
{
register int x;
/*
* Upper is the array of
* sample values which 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 < Xres;x++) {
SquareStack[0].x = (float)x;
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.
*/
subdivide_square(color)
Color *color;
{
register pixel_square *Stackp;
register float x, y;
float size, halfsize;
double avfact, xdelta, ydelta;
Color ul, ur, ll, lr, u, d, l, r, c;
extern unsigned long SuperSampled;
color->r = color->g = color->b = 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;
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 = (double)(x + halfsize);
ydelta = (double)(y + halfsize);
trace_point(xdelta, (double)y, &u);
trace_point((double)x, ydelta, &l);
trace_point(xdelta, ydelta, &c);
trace_point((double)(x + size),ydelta, &r);
trace_point(xdelta, (double)(y + size), &d);
if(size == 1.)
SuperSampled++;
push_square(x, y, halfsize, ul, u, l, c);
push_square((float)xdelta, y, halfsize, u, ur, c, r);
push_square(x, (float)ydelta, halfsize, l, c, ll, d);
push_square((float)xdelta, (float)ydelta, halfsize,
c, r, d, lr);
} while (Stackp != SquareStack);
}
/*
* Trace a ray through x, y on the screen, placing the result in "color."
*/
trace_point(x, y, color)
double x, y;
Color *color;
{
double dist;
HitInfo hitinfo;
extern Vector scrnx, scrny;
extern double aperture;
extern unsigned long EyeRays;
extern double TraceRay();
/*
* Calculate ray direction.
*/
EyeRays++;
TopRay.dir.x = firstray.x + x*scrnx.x - y*scrny.x;
TopRay.dir.y = firstray.y + x*scrnx.y - y*scrny.y;
TopRay.dir.z = firstray.z + x*scrnx.z - y*scrny.z;
(void)normalize(&TopRay.dir);
if (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 = TraceRay((Primitive *)NULL, &TopRay, &hitinfo);
ShadeRay(&hitinfo, &TopRay, dist, &background, color, 1.0);
}
/*
* Return TRUE if this pixel is okay and doesn't need to be supersampled,
* FALSE otherwise.
*/
pixel_ok(w,x,y,z)
Color *w, *x, *y, *z;
{
double rmax, rmin, gmax, gmin, bmax, bmin;
double 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 < RedContrast) &&
(gsum == 0. || (bmax - bmin) / gsum < BlueContrast) &&
(bsum == 0. || (gmax - gmin) / bsum < GreenContrast))
return TRUE;
return FALSE;
}
#ifdef LINDA
dist_worker()
{
extern unsigned long EyeRays, ShadowRays, ReflectRays, RefractRays;
extern unsigned long CacheWorked, CacheFailed, ShadowHits;
extern unsigned long SuperSampled, BVTests, HitRays;
extern unsigned long primtests[PRIMTYPES], primhits[PRIMTYPES];
extern int VerboseWorker;
extern FILE *fstats;
double user, sys;
while (dist_job())
;
/*
* Out statistics.
*/
out ("statistics", EyeRays, ShadowRays, ReflectRays, RefractRays,
CacheWorked, CacheFailed, ShadowHits, SuperSampled, BVTests,
HitRays);
/*
* Out ray/primitive intersection counts.
*/
out ("counts", primtests, primhits);
/*
* Compute running time.
*/
get_cpu_time(&user, &sys);
out ("timing", user, sys);
return;
}
/*
* Worker trained to perform distributed ray-tracing.
*/
dist_job()
{
int y;
extern int VerboseWorker;
in("scaninfo", ? y);
if (y < 0) {
out("scaninfo", y);
return 0;
}
if (VerboseWorker)
fprintf(stderr,"Worker: inned %d\n",y);
out("scaninfo", y-1);
trace_jit_line(y, out_buf);
if (VerboseWorker)
fprintf(stderr,"Worker: outing %d\n",y);
out("result", y, out_buf : Xres);
return 1;
}
adapt_worker()
{
extern unsigned long EyeRays, ShadowRays, ReflectRays, RefractRays;
extern unsigned long CacheWorked, CacheFailed, ShadowHits;
extern unsigned long SuperSampled, BVTests, HitRays;
extern unsigned long primtests[PRIMTYPES], primhits[PRIMTYPES];
double user, sys;
while (adapt_job(FALSE))
;
/*
* Out statistics.
*/
out ("statistics", EyeRays, ShadowRays, ReflectRays, RefractRays,
CacheWorked, CacheFailed, ShadowHits, SuperSampled, BVTests,
HitRays);
/*
* Out ray/primitive intersection counts.
*/
out ("counts", primtests, primhits);
/*
* Compute running time.
*/
get_cpu_time(&user, &sys);
out ("timing", user, sys);
return;
}
adapt_job(supervisor)
int supervisor;
{
int lastpix, lastscan;
extern int VerboseWorker;
in("scaninfo", ? lastpix, ? lastscan);
if (lastpix <= 0) {
out("scaninfo", lastpix, lastscan);
if (VerboseWorker)
fprintf(stderr,"Worker: all finished!\n");
return FALSE;
}
if (rdp("scanline", lastpix -1, ? pixel_buf[0]:) &&
inp("scanline", lastpix, ? pixel_buf[1]:)) {
lastpix--;
out("scaninfo", lastpix, lastscan);
if (VerboseWorker)
fprintf(stderr,"%s: doing pixline %d\n",
supervisor ? "Supervisor" : "Worker",
lastpix);
subdivide_line(lastpix, pixel_buf[0], pixel_buf[1],
out_buf);
out("result", lastpix, out_buf : Xres);
} else if (supervisor) {
/*
* Don't let the supervisor get caught up in
* ray-tracing a whole scanline. It might take
* a long, long time, causing tuple-space to get
* jammed with finished pixlines, and...
*/
if (VerboseWorker)
fprintf(stderr,"Supervisor: nothing to do...\n");
out ("scaninfo", lastpix, lastscan);
return FALSE;
} else if (lastscan > 0) {
lastscan--;
out("scaninfo", lastpix, lastscan);
if (VerboseWorker)
fprintf(stderr,"Worker: doing scan %d\n",
lastscan);
trace_line(lastscan, pixel_buf[0]);
out("scanline", lastscan, pixel_buf[0] : Xres + 1);
} else {
/*
* Nothing to do until somebody finishes a scanline.
*/
if (VerboseWorker) {
fprintf(stderr,"Worker idle... ");
fprintf(stderr,"pix = %d, scan = %d\n",
lastpix, lastscan);
}
out("scaninfo", lastpix, lastscan);
sleep(2);
}
return 1;
}
#endif