[net.sources] one more time... ray tracing

fritzz@net1.UCSD.EDU (john) (08/27/86)

well, it seems that between a few typo's in my old source, news
truncating the articles, and our news acting strange, it is belatedly
time for son of son of tracer. I keep on promising that I won't post
these anymore, but demand stays demanding, so here it is:
tracer 2.1
it is in three shars, with this the first one holding all code and
header files, and the other two holding support documents and data files
including the much demanded susie (you guys are all sick, it's only
black and white =)
so, without further ado:
----------------------------------------------


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by wizard!fritzz on Wed Aug 27 13:03:37 PDT 1986
# Contents:  find.c g_bal.c g_bod.c refract.c shade.c support.c tracer.c
#	extern.h macros.h rtd.h
 
echo x - find.c
sed 's/^@//' > "find.c" <<'@//E*O*F find.c//'
#include <math.h>
#include "rtd.h"
#include "extern.h"
#include "macros.h"


double  findo (m, s) /* finds where a ray inside the ball exits. */
struct mat *m;
struct sphere  *s;
{
/* foops id the rotated position vector. */
    struct vector   foops;
    double  t;
    MTV (foops, (*m), s -> cent);
/* see if it hits the ball (it better)*/
    t = s -> rad * s -> rad - foops.y * foops.y - foops.z * foops.z;
    if (t > 0)
	t = foops.x + sqrt (t);
    else
	t = 0;
/* return how far along the ray you were when you hit */
    return (t);
}

double  find (m, s)/* finds whether a ray hits a ball*/
struct mat *m;
struct sphere  *s;
{
    struct vector   foops;
    double  t;
    MTV (foops, (*m), s -> cent);
    t = s -> rad * s -> rad - foops.y * foops.y - foops.z * foops.z;
    if (t > 0)
	t = foops.x - sqrt (t);
    else
	t = 0;
    return (t);
}

double  finds (m, s)/* finds if a ball is between a point and a 
			lightsource. Returns how obscuring the ball is */
struct mat *m;
struct sphere  *s;
{
    struct vector   foops;
    double  t;
    MTV (foops, (*m), s -> cent);
    t = s -> rad - sqrt (foops.y * foops.y + foops.z * foops.z);
    if (t > 0)
	t = t / foops.x;
    else
	t = 0;
    return (t);
}




double  shadow (p)/* finds if a point is in a shadow, or if it is on edge */
struct vector  *p;
{
    struct mat  trans;
    struct sphere   ss;
    struct vector   d;
    int     c,
            i;
    double  l,
            k,
            x,
            y,
            z,
            finds ();
    l = 0.0;
    c = -1;
    SV (d, ls.cent, (*p));
    d.l = LEN (d);
    d.xzl = XZL (d);
    mt (&(d), &trans);

    for (i = 0; i < nob; i++) {
	ss.rad = bl[i] -> s.rad;
	SV (ss.cent, bl[i] -> s.cent, (*p));
	if ((k = finds (&trans, &ss)) > l) {
	    c = i;
	    l = k;
	}
    }
    if (c == -1)
	k = 200.0;
    else {
	k = 1.0 - l / ((ls.rad) / (d.l));
	if (k < 0.0)
	    k = 0.0;
	k *= 200.0;
    }
    return (k);
}
@//E*O*F find.c//
chmod u=rw,g=r,o=r find.c
 
echo x - g_bal.c
sed 's/^@//' > "g_bal.c" <<'@//E*O*F g_bal.c//'
#include <stdio.h>
#include "rtd.h"
#include "extern.h"
g_bal (df)
FILE * df;
{
    int     i;
    double  x,
            y,
            z,
            r,
            ior,
            rfr,
            rfl,
            dif,
            amb;
    for (i = 0;
	    fscanf (df, "%F %F %F %F %F %F %F %F %F",
		&x, &y, &z, &r, &ior, &rfr, &rfl, &dif, &amb) != EOF;
	    i++) {
	bl[i] = (struct ball   *) malloc (sizeof (struct ball));
	bl[i] -> s.cent.x = x;
	bl[i] -> s.cent.y = y;
	bl[i] -> s.cent.z = z;
	bl[i] -> s.rad = r;
	bl[i] -> ior = ior;
	bl[i] -> rfr = rfr;
	bl[i] -> rfl = rfl;
	bl[i] -> dif = dif;
	bl[i] -> amb = amb;
    }
    return (i);
}

@//E*O*F g_bal.c//
chmod u=rw,g=r,o=r g_bal.c
 
echo x - g_bod.c
sed 's/^@//' > "g_bod.c" <<'@//E*O*F g_bod.c//'
#include <stdio.h>
#include <math.h>
#include "extern.h"
#include "macros.h"


g_bod (f)
FILE * f;
{
    int     k,
            x;
    double  big = 0.0,
            little = HUGE;
    char    buf[512];


    for (ysue = 0;; ysue++) {
	if (fgets (buf, 512, f) == NULL)
	    break;
	xsue = strlen (buf) - 1;
	for (x = 0; x < xsue; x++) {
	    k = buf[x];
	    suzie[x][ysue] = (double) k;
	    if (big < k)
		big = k;
	    if (little > k)
		little = k;
	}
    }
    big = big - little;
    for (k = 0; k < ysue; k++)
	for (x = 0; x < xsue; x++)
	    suzie[x][k] = (suzie[x][k] - little) / big;
}
@//E*O*F g_bod.c//
chmod u=rw,g=r,o=r g_bod.c
 
echo x - refract.c
sed 's/^@//' > "refract.c" <<'@//E*O*F refract.c//'
#include <math.h>
#include "rtd.h"
#include "macros.h"
#include "extern.h"

int	rlev;
int     refract (r, bll)
struct ray *r;
struct ball *bll;
{
    struct vector   new,
                    norm;
    struct mat  trans;
    struct ray  ir;
    double  l,
            refk (), getcapt (), capt, inside ();
    double  stupid;
    struct sphere   ss;

    SV (norm, r -> org, bll -> s.cent);
    norm.l = bll-> s.rad;

    capt = getcapt (&norm, &(r -> dir), bll -> ior);


/* get the addition factor for the normal for refraction*/
    stupid = refk (&(norm), &(r -> dir), bll -> ior);
    SCMLT (stupid, norm);

    AV (ir.dir, r -> dir, norm);
    MV (r -> org.x, r -> org.y, r -> org.z, ir.org);

/* now get it for reflection */
    SV (norm, r -> org, bll -> s.cent);
    norm.l = bll -> s.rad;
    SCMLT (1.0 / norm.l, norm);
    stupid = 2.0 * DOT (norm, r -> dir);
    SCMLT (stupid, norm);
    SV (r -> dir, r -> dir, norm);

    return ((int) ((1.0 - capt) * (double) shade (r) + ((capt) * inside (&ir, bll))));
}

double  inside (r, bll)
struct ray *r;
struct ball *bll;
{
    struct vector   new,
                    norm;
    struct mat  trans;
    struct ray  er;
    double  findo (), lght, l, refk (), getcapt (), capt;
    double  stupid;
    struct sphere   ss;


    if (++rlev < RLEV) {
	r -> dir.l = LEN (r -> dir);
	r -> dir.xzl = XZL (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 (er.org, r -> org, new);
	AV (r -> org, r -> org, new);
	SV (norm, er.org, bll -> s.cent);

	norm.l = bll -> s.rad;
	capt = getcapt (&norm, &(r -> dir), 1.0 / bll -> ior);

	stupid = refk (&norm, &(r -> dir), 1.0 / bll -> ior);
	SCMLT (stupid, norm);
	AV (er.dir, norm, r -> dir);

	SCMLT (1.0 / norm.l, norm);
	stupid = 2.0 * DOT (norm, r -> dir);
	SCMLT (stupid, norm);
	SV (r -> dir, r -> dir, norm);
	lght = (1.0 - capt) * inside (r, bll) + (capt * (double) shade (&er));
    }
    else
	lght = 0.0;
    rlev--;
     if (lght<0.0) lght=0.0;
     if (lght>255.0) lght=255.0;
    return (lght);
}



double  refk (nrm, in, ior)
struct vector  *nrm,
               *in;
double  ior;
{
    double  dt,
            ln,
            li,
            ret;

    ior = ior * ior;
    dt = DOT ((*nrm), (*in));
    ln = LN2 ((*nrm));
    li = LN2 ((*in));
    if (dt < 0)
	ret = (-dt - sqrt (dt * dt - ln * li * (1 - ior))) / ln;
    else
	ret = (-dt + sqrt (dt * dt - ln * li * (1 - ior))) / ln;
    return (ret);
}

double  getcapt (nrm, dr, ior)
struct vector  *nrm,
               *dr;
double  ior;
{
    double  dt,
            cs1,
            cs2,
            p,
            s;
    dt = DOT ((*nrm), (*dr));
    dt = dt * dt / LN2 ((*nrm)) / LN2 ((*dr));
    cs1 = sqrt (dt);
    cs2 = sqrt (1.0 - (1.0 - dt) / ior);
    p = cs1 / (cs1 + ior * cs2);
    s = cs1 / (ior * cs1 + cs2);
    return (2.0 * (p * p + s * s));
}
@//E*O*F refract.c//
chmod u=rw,g=r,o=r refract.c
 
echo x - shade.c
sed 's/^@//' > "shade.c" <<'@//E*O*F shade.c//'
/*
 * this subroutine does all the gritty work- it calculates 
 * what shade each pixel should be. I like recursion.
 */
#include <math.h>
#include "rtd.h"
#include "macros.h"
#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 (), shadow ();
    int     sx,
            sy;
    double  stupid;
    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() */
	r -> dir.l = LEN (r -> dir);
	r -> dir.xzl = XZL (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 && (l * trans.x.y + r -> org.y) > 0.0) {
				/* WE HIT SOMETHING */
	    MV (l * trans.x.x, l * trans.x.y, l * trans.x.z, new);
	    new.l=l;
/* 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);
	    norm.l=bl[c] ->s.rad;

/* ambient lighting */
	    lght = 200.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);
		new.l = LEN(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) {
/* make the normal unit length */
		SCMLT ((1.0 / norm.l), norm);
/* get the length of the ray's component in the normal direction */
		stupid = 2.0 * DOT (norm, r -> dir);
		SCMLT (stupid, 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;

		SV (new, ls.cent, r -> org);
		new.l = LEN(new);
		sx = (int) (r -> org.x / 1.5) % xsue;
		if (sx < 0)
		    sx += xsue;
		sy = -(int) (r -> org.z / 1.5) % ysue;
		if (sy < 0)
		    sy += ysue;
		lght = (sam * suzie[sx][sy] + 1.0 - sam) * (0.8 *
			shadow (&(r -> org)) * (new.y) / (new.l) + 40.0);


	    }
	    else {		/* check to see if it hit lightsource */
		SV (ss.cent, ls.cent, r -> org);
		ss.rad = ls.rad;
		if (find (&trans, &(ss.cent)) > 0.0)
		    lght = 255;
		else
		    lght = 0;
	    }
	}
    }
/* 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);
}
@//E*O*F shade.c//
chmod u=rw,g=r,o=r shade.c
 
echo x - support.c
sed 's/^@//' > "support.c" <<'@//E*O*F support.c//'
/*
 *    supportive subroutines...
 */

#include <math.h>
#include <stdio.h>
#include "rtd.h"
#include "extern.h"


mt (vec, trans)
struct vector  *vec;
struct mat *trans;
{
    if (vec -> xzl == 0.0) {
	trans -> x.x = 0.0;
	trans -> x.y = 1.0;
	trans -> x.z = 0.0;
	trans -> y.x = -1.0;
	trans -> y.y = 0.0;
	trans -> y.z = 0.0;
	trans -> z.x = 0.0;
	trans -> z.y = 0.0;
	trans -> z.z = 1.0;
    }
    else {
	trans -> x.x = (vec -> x) / (vec -> l);
	trans -> x.y = (vec -> y) / (vec -> l);
	trans -> x.z = (vec -> z) / (vec -> l);
	trans -> y.x = -(vec -> x) * (vec -> y) / ((vec -> l) * (vec -> xzl));
	trans -> y.y = (vec -> xzl) / (vec -> l);
	trans -> y.z = -(vec -> z) * (vec -> y) / ((vec -> l) * (vec -> xzl));
	trans -> z.x = -(vec -> z) / (vec -> xzl);
	trans -> z.y = 0;
	trans -> z.z = (vec -> x) / (vec -> xzl);
    }
}
@//E*O*F support.c//
chmod u=rw,g=r,o=r support.c
 
echo x - tracer.c
sed 's/^@//' > "tracer.c" <<'@//E*O*F tracer.c//'


/* tracer version 2.1 */
#include <stdio.h>
#include <math.h>
#include "rtd.h"
#include "macros.h"


FILE * fp;
double  suzie[300][300],
        sam = 1.0;
int     xsue,
        ysue;
struct ball *bl[150];
int     level,
        nob;
struct sphere   ls;

main (argc, argv)
int     argc;
char  **argv;
{
    FILE * df, *texfile;
    static double   xco,
                    yco;
    struct ray  rr;
    struct vector   vp;
    double  x,
            y,
            z;
    int     i,
            in = 0,
            out = 0,
            tex = 0;
    int     c;

/* command interp */

    for (i = 1; i < argc; i++) {
	if (argv[i][0] != '-')
	    booboo ("Options strt with a '-' bozo");
	c = argv[i][1];

	switch (c) {
	    case ('i'): 
		if (in)
		    booboo ("Sorry, but you may only have one input file");
		in = 1;
		if ((i + 1) >= argc || argv[i + 1][0] == '-')/* no arg */
		    df = stdin;
		else
		    if ((df = fopen (argv[++i], "r")) == NULL)
			booboo ("input file not found");
		break;
	    case ('o'): 
		if (out)
		    booboo ("Sorry, but you may have only one output file");
		out = 1;
		if ((i + 1) >= argc || argv[i + 1][0] == '-')/* no arg */
		    fp = stdout;
		else
		    fp = fopen (argv[++i], "w");
		break;
	    case ('s'): 
		if (tex)
		    booboo ("Sorry, but you may have only one image file");
		if ((i + 1) >= argc || argv[i + 1][0] == '-')/* no arg */
		    booboo ("-s requires an argument");
		tex = 1;
		if ((texfile = fopen (argv[++i], "r")) == NULL)
		    booboo ("image file not found");
		break;
		booboo ("this line shouldn't do anything");
	    case ('S'): 
		if (argv[i][2] < '0' || argv[i][2] > '9'){
printf("%c\n",argv[i][2]);
		    booboo ("-S needs a numerical argument");}
		sam = atof (&(argv[i][2]));
		break;
	    default: 
		booboo ("Unrecognized option. Better try again");
	}
    }


    if (!in)
	if ((df = fopen ("bdata.i", "r")) == NULL)
	    booboo ("bdata.i not found");
    if (!out)
	fp = fopen ("data.dis", "w");
    if (!tex)
	if ((texfile = fopen ("pat.def", "r")) == NULL)
	    booboo ("pat.def not found");



    nob = g_bal (df);
    g_bod (texfile);



    MV (95.0, 140.0, -200.0, vp);
    MV (0.0, 900.0, 0.0, ls.cent);
    ls.rad = 40;
    fprintf (fp, "%d %d\n", (int) ((XMAX - XMIN) * SCALE +0.9999999), (int) ((YMAX - YMIN) * SCALE +0.9999999));

    for (yco = YMAX * SCALE; yco > YMIN * SCALE; yco--)
	for (xco = XMIN * SCALE; xco < XMAX * SCALE; xco++) {
	    MV (xco / SCALE, yco / SCALE, 0.0, rr.org);
	    SV (rr.dir, rr.org, vp);
	    fprintf (fp, "%c", shade (&rr));
	}
}

booboo (str)
char   *str; {
    printf ("%s\n", str);
    exit (-1);
}
@//E*O*F tracer.c//
chmod u=rw,g=r,o=r tracer.c
 
echo x - extern.h
sed 's/^@//' > "extern.h" <<'@//E*O*F extern.h//'
extern double suzie[300][300],sam;
extern struct ball *bl[];
extern struct sphere ls;
extern int level,nob;
extern int xsue,ysue;
@//E*O*F extern.h//
chmod u=rw,g=r,o=r extern.h
 
echo x - macros.h
sed 's/^@//' > "macros.h" <<'@//E*O*F macros.h//'
/* some of the most important stuff in the program */
#define DOT(v1,v2) (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z)
/* returns dot product of two vectors */
#define LN2(v)	   (DOT(v,v))
/* returns the square of the length of a vector */
#define LEN(v)	   sqrt(LN2(v))
/* guess */
#define XZL(v)	   sqrt(v.x*v.x+v.z*v.z)
/* returns the component in the xz plane of a vector */
#define SCMLT(sc,vct) vct.x*= sc;vct.y*= sc;vct.z*= sc;vct.l*= sc;
/* multiplies a vetor by a scalar */
#define MV(a,b,c,v)   v.x= a;v.y= b;v.z= c;
/* makes a vector. wouldn't need this with c++ */
#define SV(t,u,v)  t.x=u.x-v.x;t.y=u.y-v.y;t.z=u.z-v.z;
/*subtract vector t=u-v */
#define AV(t,u,v)  t.x=u.x+v.x;t.y=u.y+v.y;t.z=u.z+v.z;
/* add vector t=u+v */
#define MTV(v1,m,v2) MV(DOT(m.x,v2),DOT(m.y,v2),DOT(m.z,v2),v1)
/* multiply transpose matrix by vector. v1=m*v2 */

#define LEVEL 5/* levels of recursion */
#define RLEV  3/*don't want as many inside the ball, takes forever as it is*/

#define XMIN 10.0
#define XMAX 220.0
#define YMIN 10.0
#define YMAX 170.0
/* window size,  virtual units */
#define SCALE  2.0
/* maginification factor */
@//E*O*F macros.h//
chmod u=rw,g=r,o=r macros.h
 
echo x - rtd.h
sed 's/^@//' > "rtd.h" <<'@//E*O*F rtd.h//'
struct color {
int r;int g;int b;};

struct vector {
double x;
double y;
double z;
double l;
double xzl;} ;

struct ray {
struct vector org;
struct vector dir;} ;

struct sphere {
struct vector cent;
double rad;} ;

struct ball {
struct sphere s;
double ior;
double rfr;
double rfl;
double dif;
double amb;
};

struct mat {
struct vector x;  /* first !row! */
struct vector y;  /*second !row! */
struct vector z;}; /* third !row! */

@//E*O*F rtd.h//
chmod u=rw,g=r,o=r rtd.h
 
exit 0