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)); }