basic ray tracing: part 2 (shade.c)
Friedrich Knauss
fritzz at net1.UCSD.EDU
Sat Jul 26 07:37:02 AEST 1986
/*
* 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));
}
More information about the Comp.sources.unix
mailing list