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.