[net.sources] basic ray tracing: part 2

fritzz@net1.UCSD.EDU (Friedrich Knauss) (07/26/86)

/*
 * this subroutine does all the gritty work- it calculates 
 * what shade each pixel should be. I like recursion.
 */
#include <math.h>
#include "rtd.h"
/* LEVEL defines number of levels of recursion */
#define LEVEL 5

#include "extern.h"

int     shade (r)
struct ray *r;
{
    int     i,
            c,
            refract ();
    struct ray  refr;
    double  lght,
            x,
            y,
            z,
            l,
            k,
            dot (), find (), findo (), shadow ();
    struct vector   new,
                    norm;
    struct mat  trans;
    struct sphere   ss;
    if (++level <= LEVEL) {
	c = -1;
	l = HUGE;
/* get vector length and xz component for mt() */
	vecl (&(r -> dir));
	vexzl (&(r -> dir));
/* make a transform matrix that rotates something in space so
   that the ray will be aligned with the x axis */
	mt (&(r -> dir), &trans);

/* for starters we find out whether we hit anything. */
	for (i = 0; i < nob; i++) {
	    ss.rad = bl[i].s.rad;
	    sv (&(ss.cent), &(bl[i].s.cent), &(r -> org));
	    if ((k = find (&trans, &ss)) > 0.0 && k < l) {
		c = i;
		l = k;
	    }
	}
	if (c >= 0.0) {		/* WE HIT SOMETHING */
	    x = l * trans.x.x;
	    y = l * trans.x.y;
	    z = l * trans.x.z;
	    mv (x, y, z, &new);
/* move the new orgin of the ray to the intersection */
	    av (&(refr.org), &new, &(r -> org));
	    av (&(r -> org), &new, &(r -> org));
	    mv (r -> dir.x, r -> dir.y, r -> dir.z, &(refr.dir));
/* get a normal vector for the intersection point */
	    sv (&norm, &(r -> org), &(bl[c].s.cent));
	    vecl (&norm);

/* ambient lighting */
	    lght = 255.0 * bl[c].amb;

/* shaded lighting (diffuse). subroutine shadow is in find.c */
	    if (bl[c].dif != 0.0) {
		sv (&new, &(ls.cent), &(r -> org));
		vecl (&new);
		if ((k = dot (&new, &norm)) > 0.0)
		    lght += bl[c].dif * shadow (&(r -> org)) * k / (new.l) / (norm.l);
	    }

/*reflection... easy */
	    if (bl[c].rfl != 0.0) {
		vecl (&norm);
/* make the normal unit length */
		scamult ((1.0 / norm.l), &norm);
/* get the length of the ray's component in the normal direction */
		x = 2.0 * dot (&norm, &(r -> dir));
		scamult (x, &norm);
/* subtract double the normal component- !reflection! */
		sv (&(r -> dir), &(r -> dir), &norm);
		lght += bl[c].rfl * (double) shade (r);
	    }

/* refraction. this is ugly, which is why I choose to deal with
   it in it's own subroutine which comes after this one */
	    if (bl[c].rfr != 0.0) {
		lght += bl[c].rfr * (double) refract (&refr, &(bl[c]));
	    }



	}
	else {			/* hit no objects... */
	    if ((r -> dir.y) < 0.0) {/* crosses floor */
		z = -(r -> org.y) / (r -> dir.y);
		(r -> org.x) += z * (r -> dir.x);
		(r -> org.z) += z * (r -> dir.z);
		(r -> org.y) = 0.0;
		if (((int) ((r -> org.x) / 9.0) % 8 == 0) || ((int) ((r -> org.z) / 9.0) % 8 == 0))
		    lght = 0.0;	/* this is for texture, grid on the ground
				   */
		else {		/* get shading for the squares... */
		    sv (&new, &(ls.cent), &(r -> org));
		    vecl (&new);
		    lght = 0.6 * shadow (&(r -> org)) * (new.y) / (new.l) + 100.0;
		}
	    }
	    else
		lght = 0.0;	/* didn't hit ground... sky */
	}
    }
/* to many levels return 0 cause it shouldn't matter */
    else
	lght = 0;
    level--;
    if (lght < 0.0)
	lght = 0.0;
    if (lght > 255.0)
	lght = 255.0;
    return ((int) lght);
}

int     refract (r, bll)
struct ray *r;
struct ball *bll;
{
    struct vector   new,
                    norm;
    struct mat  trans;
    double  df,
            l;
    struct sphere   ss;

    sv (&norm, &(r -> org), &(bll -> s.cent));
    vecl (&norm);
    vecl (&(r -> dir));
/* this isn't real refraction, it's just a function that fits at
the extremes: dead on and barely tangential. This is definite
consideration for future work */

/* get the addition factor for the normal */
    l = dot (&norm, &(r -> dir)) / (r -> dir.l) / norm.l;
    df = (bll -> ior) * sqrt (1.0 - l * l) * (r -> dir.l) / (norm.l);
    scamult (df, &norm);
    sv (&(r -> dir), &(r -> dir), &norm);
/* find the point where the ray leaves the sphere. just like shade. */
    vexzl (&(r -> dir));
    vecl (&(r -> dir));
    mt (&(r -> dir), &trans);
    ss.rad = bll -> s.rad;
    sv (&ss.cent, &(bll -> s.cent), &(r -> org));
    l = findo (&trans, &(ss));
    mv (l * trans.x.x, l * trans.x.y, l * trans.x.z, &new);
    av (&(r -> org), &(r -> org), &new);
/* redirect the ray and continue tracing */
    sv (&norm, &(r -> org), &(bll -> s.cent));
    vecl (&norm);
    vecl (&(r -> dir));
    l = dot (&norm, &(r -> dir)) / (r -> dir.l) / norm.l;
    df = (bll -> ior) * sqrt (1.0 - l * l) * (r -> dir.l) / (norm.l);
    scamult (df, &norm);
    sv (&(r -> dir), &(r -> dir), &norm);

    return (shade (r));
}