/* From harvard!husc6!caip!nike!ucbcad!ucbvax!sdcsvax!net1!fritzz 25 Jul 86 21:35:59 GMT Article 2915 of net.sources: Relay-Version: version B 2.10.2 9/18/84; site bbnccv.UUCP Path: bbnccv!harvard!husc6!caip!nike!ucbcad!ucbvax!sdcsvax!net1!fritzz >From: fritzz@net1.UCSD.EDU (Friedrich Knauss) Newsgroups: net.sources Subject: basic ray tracing: part 1 (tracer.c) Message-ID: <190@net1.UCSD.EDU> Date: 25 Jul 86 21:35:59 GMT Date-Received: 27 Jul 86 01:07:29 GMT Reply-To: fritzz@net1.UUCP (Friedrich Knauss) Organization: University of California, San Diego */ /* * this subroutine does all the gritty work- it calculates * what shade each pixel should be. I like recursion. */ #include #include "rtd.h" #include "extern.h" #define time(x) 0 /* LEVEL defines number of levels of recursion */ #define LEVEL 5 int debug; #define dprintf if (debug) printf shade (r, red, green, blue) struct ray *r; double *red, *green, *blue; { int i, c, refract (); struct ray refr; double rd, gn, blu, tmp; double lght_red, lght_green, lght_blue, x, y, z, l, k, dot (), find (), findo (); struct vector new, norm; struct mat trans; struct sphere ss; if (++level <= LEVEL) { c = -1; l = HUGE; dprintf("LEVEL %d: ", level); /* 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 */ tmp = 255.0 * bl[c].amb; lght_red = tmp * bl[c].red; lght_green = tmp * bl[c].green; lght_blue = tmp * bl[c].blue; dprintf(" ball[%d]", c); dprintf(", ambient=%d,%d,%d\n", (int)lght_red, (int)lght_green, (int)lght_blue); /* 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) { shadow (&r->org, &rd, &gn, &blu); dprintf("diffuse = %d, %d, %d\n",(int)rd,(int)gn,(int)blu); tmp = bl[c].dif * k / (new.l * norm.l); lght_red += tmp * rd * bl[c].red; lght_green += tmp * rd * bl[c].green; lght_blue += tmp * rd * bl[c].blue; } } /*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); shade (r, &rd, &gn, &blu); dprintf("reflection = %d, %d, %d, tmp=%d\n", (int)rd, (int)gn, (int)blu, (int)tmp); tmp = bl[c].rfl; lght_red += tmp * rd; lght_green += tmp * gn; lght_blue += tmp * blu; } /* 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) { dprintf(" refraction\n"); refract (&refr, &bl[c], &rd, &gn, &blu); tmp = bl[c].rfr; dprintf("refraction = %d, %d, %d\n",(int)rd,(int)gn,(int)blu); lght_red += tmp * rd; lght_green += tmp * gn; lght_blue += tmp * blu; } } 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; dprintf("floor (%d, %d): ", (int)r->org.x, (int)r->org.z); if ( ((int) ((r->org.x) / 9.0) % 8 == 0) || ((int) ((r->org.z) / 9.0) % 8 == 0)) { lght_red = lght_green = lght_blue = 0.0; dprintf("black. 0,0,0\n"); } /* this is for texture, grid on the ground */ else /* get shading for the squares... */ { sv (&new, &ls.cent, &r->org); vecl (&new); shadow (&r->org, &rd, &gn, &blu); tmp = 0.6 * new.y / new.l; lght_blue = tmp * blu + 100.0; /* tile pattern: */ if ((int)r->org.x/72 & (int)r->org.z/72 & 0x15) { lght_red = lght_green = 0.0; /* blue */ dprintf("blue. %d, %d, %d\n", (int)lght_red, (int)lght_green, (int)lght_blue); } else { lght_red = tmp * rd + 100.0; /* white */ lght_green = tmp * gn + 100.0; dprintf("white. %d,%d,%d\n", (int)lght_red, (int)lght_green, (int)lght_blue); } } } else /* didn't hit ground... sky */ { lght_red = lght_green = lght_blue = 0.0; dprintf("sky. %d,%d,%d\n", (int)lght_red, (int)lght_green, (int)lght_blue); } dprintf("lght_= %d,%d,%d\n", (int)lght_red, (int)lght_green, (int)lght_blue); } } /* to many levels return 0 cause it shouldn't matter */ else { lght_red = lght_green = lght_blue = 0.0; dprintf(">5 levels. 0, 0, 0\n"); } level--; if (lght_red < 0.0) lght_red = 0.0; if (lght_red > 255.0) lght_red = 255.0; if (lght_green < 0.0) lght_green = 0.0; if (lght_green > 255.0) lght_green = 255.0; if (lght_blue < 0.0) lght_blue = 0.0; if (lght_blue > 255.0) lght_blue = 255.0; *red = lght_red; *green = lght_green; *blue = lght_blue; dprintf("returned: %d, %d, %d\n", (int)*red, (int)*green, (int)*blue); } int refract (r, bll, red, green, blue) struct ray *r; struct ball *bll; double *red, *green, *blue; { struct vector new, norm; struct mat trans; double l, refk (); struct sphere ss; sv (&norm, &r->org, &bll->s.cent); vecl (&norm); vecl (&r->dir); /* get the addition factor for the normal */ scamult (refk (&norm, &r->dir, bll->ior), &norm); av (&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); scamult (refk (&norm, &r->dir, 1.0 / bll->ior), &norm); av (&r->dir, &r->dir, &norm); shade (r, red, green, blue); } double refk (nrm, in, ior) struct vector *nrm, *in; double ior; { double dt, ln, li, ret; dt = dot (nrm, in); ln = nrm->x * nrm->x + nrm->y * nrm->y + nrm->z * nrm->z; li = in->x * in->x + in->y * in->y + in->z * in->z; ret = dt * dt - ln * li * (1 - ior); if (ret < 0.0) ret = 0.0; if (dt < 0) ret = (-dt - sqrt (ret)) / ln; else ret = (-dt + sqrt (ret)) / ln; return (ret); } printv(s, v) char *s; struct vector *v; { printf("%s.x = %f, .y = %f, .z = %f\n", s, v->x, v->y, v->z); }