public double[] trace(Ray ray, int depth) {
double[] illum = { 0, 0, 0 };
double closestHitDistance = 9999;
Solid solidHit = null;
double[] objHitDistance = new double[2];
// check intersection of all the objects to find the closest one
for (int i = 0; i < solidList.size(); i++) {
Solid solid = (Solid) solidList.elementAt(i);
int numHits = solid.intersect(ray, objHitDistance);
if (numHits > 0) {
if (objHitDistance[0] < closestHitDistance) {
closestHitDistance = objHitDistance[0];
solidHit = solid;
}
}
}
// a solid was hit -- continue with calculations
if (solidHit != null) {
// calculate hit point
Point hitPoint = new Point(ray.sx + closestHitDistance * ray.dx,
ray.sy + closestHitDistance * ray.dy,
ray.sz + closestHitDistance * ray.dz);
// calculate normal
Vect normal = solidHit.normal(hitPoint); // already normalized
// get the texture of the solid hit
Texture hitTexture = null;
Object[] param = { hitPoint, solidHit };
try {
hitTexture = (Texture)solidHit.texture.invoke(null, param);
} catch (Exception e) {
System.out.println("Texture Error!");
e.printStackTrace();
}
// BEGIN LOOP THROUGH LIGHTS
for (int i = 0; i < lightList.size(); i++) {
Light light = (Light) lightList.elementAt(i);
// ambient light
if (light.type == Light.AMBIENT) {
illum[0] += hitTexture.r * light.r;
illum[1] += hitTexture.g * light.g;
illum[2] += hitTexture.b * light.b;
}
// point light
else if (light.type == Light.POINT) {
// check to see if the hitpoint is in the shadows
Ray rayToLight = new Ray(hitPoint.x + TINY*normal.x, // move hitpoint away from
hitPoint.y + TINY*normal.y, // surface a small amount
hitPoint.z + TINY*normal.z,
light.x - hitPoint.x,
light.y - hitPoint.y,
light.z - hitPoint.z);
rayToLight.normalize();
boolean inShadow = false;
double[] a = new double[2];
// check for shadows
for (int j = 0; j < solidList.size(); j++) {
Solid solid = (Solid) solidList.elementAt(j);
// (ensure hitpoint is in front of camera)
if (solid.intersect(rayToLight, a) > 0 && a[0] > 0) {
inShadow = true;
break;
}
}
// if not in shadow, calculate light
if (!inShadow) {
double cos = (light.x*normal.x + light.y*normal.y + light.z*normal.z) /
Math.sqrt(light.x*light.x + light.y*light.y + light.z*light.z) /
Math.sqrt(normal.x*normal.x + normal.y*normal.y + normal.z*normal.z);
if (cos < 0) cos = 0;
// add light from point light source
illum[0] += hitTexture.r * light.r * cos;
illum[1] += hitTexture.g * light.g * cos;
illum[2] += hitTexture.b * light.b * cos;
}
}
} // END LOOP THROUGH LIGHTS
if (depth < 1) {
// reflection
if (hitTexture.refl > 0) {
Vect eyeVect = new Vect(-ray.dx, -ray.dy, -ray.dz); // already normalized
Vect reflVect = new Vect();
double nDotE = normal.x*eyeVect.x + normal.y*eyeVect.y + normal.z*eyeVect.z;
reflVect.x = 2*nDotE*normal.x - eyeVect.x;
reflVect.x = 2*nDotE*normal.y - eyeVect.y;
reflVect.x = 2*nDotE*normal.z - eyeVect.z;
reflVect.normalize();
// move hitpoint out from hit surface a small amount
Point hp = new Point(hitPoint.x + TINY*normal.x,
hitPoint.y + TINY*normal.y,
hitPoint.z + TINY*normal.z);
hp.normalize();
// cast new reflected ray from hit point to reflected direction
Ray reflRay = new Ray(hp.x, hp.y, hp.z, reflVect.x, reflVect.y, reflVect.z);
reflRay.normalize();
double[] reflIllum = trace(reflRay, depth + 1);
illum[0] += reflIllum[0] * hitTexture.refl;
illum[1] += reflIllum[1] * hitTexture.refl;
illum[2] += reflIllum[2] * hitTexture.refl;
// check bounds and round for illum (0.0 < illum < 1.0)
for (int i = 0; i < 3; i++)
if (illum > 1) illum = 1;
}
}
return illum;
}
// no solids were hit -- return background color
else {
return illum;
}
}
/**
* Render the picture.
*/
public void paint(Graphics g) {
double sx, sy;
double hei = getHeight(), wid = getWidth();
for (int x = 0; x < wid; x++) {
for (int y = 0; y < hei; y++) {
sx = (x - wid/2) / (wid/2);
sy = (hei/2 - y) / (wid/2);
Ray ray = new Ray(0,0,5,sx,sy,-5);
ray.normalize();
// trace the ray
double[] illum = trace(ray, 0);
g.setColor(new Color((float)illum[0],(float)illum[1],(float)illum[2]));
g.fillRect(x, y, 1, 1);
}
}
}
class Ray
{
double sx, sy, sz;
double dx, dy, dz;
public Ray() {}
public Ray(double sx, double sy, double sz, double dx, double dy, double dz)
{
this.sx = sx;
this.sy = sy;
this.sz = sz;
this.dx = dx;
this.dy = dy;
this.dz = dz;
}
public void normalize()
{
double t = Math.sqrt(dx*dx + dy*dy + dz*dz);
if (t != 0)
{
dx /= t;
dy /= t;
dz /= t;
}
}
}