import java.awt.*; public class AdvRayTrace extends PixApplet { // CAMERA PROPRTIES double camera[] = {0,0,0}; // location of camera int focalLength = 2; // focal length double p = 1; // Specular highlight power double bgColor[] = {0.8, 1, 0.5}; Matrix3D matrix = new Matrix3D(); double spheres[][] = { {0.15, 0.0, -0.5, 0.1}, {-0.15, 0.0, -0.5, 0.1}, {0.0, 0.15, -0.5, 0.1}, {0.0, -0.15, -0.5, 0.1}, //{0.0, 0.0, 0.5, 0.2}, //{0.0, 0.0, -0.5, 0.1}, }; double[][] spheresCopy = spheres; double diffuseColors[][] = { {0.3, 0.3, 0.3}, {0.9, 0.3, 0.3}, {0.3, 0.9, 0.3}, {0.3, 0.3, 0.9}, //{0.6, 0.6, 0.3}, //{0.3, 0.6, 0.6}, }; double specularColors[][] = { {0.2, 0.2, 0.2}, {0.8, 0.4, 0.4}, {0.4, 0.9, 0.4}, {0.4, 0.4, 0.8}, //{0.5, 0.5, 0.2}, //{0.2, 0.5, 0.5}, }; double[][] lights = { {1, 1, 0}, }; double[][] lightColors = { {0.8, 0.8, 0.8}, }; public void setPix(int frame) { // SET PIXELS FOR THIS ANIMATION FRAME // (THIS OVERRIDES A METHOD IN PIXAPPLET CLASS) if (xAngle != 0 || yAngle != 0) System.out.println("xA = " + xAngle + "; yA = " + yAngle); matrix.identity (); matrix.rotateY (xAngle); matrix.rotateX (yAngle); xAngle = 0; yAngle = 0; double[] transformedSphere; for (int i = 0; i < spheres.length; i++) { for (int j = 0; j < 3; j++) { spheresCopy[i][j] = spheres[i][j]; } transformedSphere = matrix.transform (spheresCopy[i]); spheres[i][0] = transformedSphere[0]; spheres[i][1] = transformedSphere[1]; spheres[i][2] = transformedSphere[2]; spheres[i][3] = spheresCopy[i][3]; } int n = 0; double w[] = new double[3]; double rgb[]; for (int j = 0 ; j < H ; j++) // LOOP OVER IMAGE ROWS for (int i = 0 ; i < W ; i++) { // LOOP OVER IMAGE COLUMNS w[0] = (double)(i - W/2) / (W/2); // COMPUTE RAY DIRECTION AT EACH PIXEL w[1] = (double)(H/2 - j) / (W/2); // w[2] = -focalLength; // PLACE IMAGE PLANE AT z = -focalLength rgb = rayTrace(camera, w, 0); // RAY TRACE AT THIS PIXEL TO GET COLOR pix[n++] = pack(Math.max(0,Math.min(255,(int)(255 * rgb[0]))), Math.max(0,Math.min(255,(int)(255 * rgb[1]))), Math.max(0,Math.min(255,(int)(255 * rgb[2])))); } } public double[] rayTrace(double v[], double w[], int num_reflections) { double rgb[] = new double[3]; double t = Double.MAX_VALUE; double newT1, newT2; double A, B, C; if (num_reflections > 10) { rgb[0] = 0.1; rgb[1] = 0.1; rgb[2] = 0.1; return rgb; } num_reflections++; int sphereSeen = -1; A = Math.pow(w[0], 2) + Math.pow(w[1], 2) + Math.pow(w[2], 2); for (int i = 0; i < spheres.length; i++) { //if (spheres[i][0] != 0) { //System.out.print("spheres[" + i + "] = {"); //System.out.print(spheres[i][0] + ", "); //System.out.print(spheres[i][1] + ", "); //System.out.print(spheres[i][2] + ", "); //System.out.print(spheres[i][3] + ", "); //System.out.print("}\n"); //} B = 2 * (v[0] - spheres[i][0]) * w[0] + 2 * (v[1] - spheres[i][1]) * w[1] + 2 * (v[2] - spheres[i][2]) * w[2]; C = Math.pow(v[0] - spheres[i][0], 2) + Math.pow(v[1] - spheres[i][1], 2) + Math.pow(v[2] - spheres[i][2], 2) - Math.pow(spheres[i][3], 2); double discriminant = Math.pow(B, 2) - 4 * A * C; if (discriminant >= 0) { // calculate the two possible intersections, choose the lowest one above zero newT1 = (-B + Math.sqrt(discriminant)) / (2 * A); newT2 = (-B - Math.sqrt(discriminant)) / (2 * A); if (newT1 < 0) newT1 = Double.MAX_VALUE; if (newT2 < 0) newT2 = Double.MAX_VALUE; if (newT2 < newT1) newT1 = newT2; // found the closest sphere if (newT1 < t) { t = newT1; sphereSeen = i; } } } if (sphereSeen == -1) { rgb[0] = bgColor[0]; rgb[1] = bgColor[1]; rgb[2] = bgColor[2]; } else { //Compute surface point: S = v + tw double s[] = {v[0] + t * w[0], v[1] + t * w[1], v[2] + t * w[2]}; // Compute surface normal n = (S-[cx,cy,cz])/r double n[] = new double[3]; n[0] = (s[0] - spheres[sphereSeen][0]) / spheres[sphereSeen][3]; n[1] = (s[1] - spheres[sphereSeen][1]) / spheres[sphereSeen][3]; n[2] = (s[2] - spheres[sphereSeen][2]) / spheres[sphereSeen][3]; // Compute reflection vector r = -2(w*n)n + w // use reflection vector as w in new ray trace double r[] = new double[3]; double wn = w[0] * n[0] + w[1] * n[1] + w[2] * n[2]; r[0] = -2 * wn * n[0] + w[0]; r[1] = -2 * wn * n[1] + w[1]; r[2] = -2 * wn * n[2] + w[2]; double epsilon = 0.01; // Create point to reflect from to be slightly above the actual surface s[0] = s[0] + epsilon * r[0]; s[1] = s[1] + epsilon * r[1]; s[2] = s[2] + epsilon * r[2]; rgb[0] = 0.2 * diffuseColors[sphereSeen][0]; rgb[1] = 0.2 * diffuseColors[sphereSeen][1]; rgb[2] = 0.2 * diffuseColors[sphereSeen][2]; for (int i = 0; i < lights.length; i++) { double[] lightDirectionVector = { lights[i][0] - s[0], lights[i][1] - s[1], lights[i][2] - s[2], }; double ln = lightDirectionVector[0] * n[0] + lightDirectionVector[1] * n[1] + lightDirectionVector[2] * n[2]; double[] highlightDirectionVector = { 2 * ln * n[0] - lightDirectionVector[0], 2 * ln * n[1] - lightDirectionVector[1], 2 * ln * n[2] - lightDirectionVector[2] }; double hDOTnegW = highlightDirectionVector[0] * -w[0] + highlightDirectionVector[1] * -w[1] + highlightDirectionVector[2] * -w[2]; rgb[0] += lightColors[i][0] * diffuseColors[sphereSeen][0] * Math.max(0, ln); rgb[0] += lightColors[i][0] * specularColors[sphereSeen][0] * Math.pow(Math.max(0, hDOTnegW), p); rgb[1] += lightColors[i][1] * diffuseColors[sphereSeen][1] * Math.max(0, ln); rgb[1] += lightColors[i][1] * specularColors[sphereSeen][1] * Math.pow(Math.max(0, hDOTnegW), p); rgb[2] += lightColors[i][2] * diffuseColors[sphereSeen][2] * Math.max(0, ln); rgb[2] += lightColors[i][2] * specularColors[sphereSeen][2] * Math.pow(Math.max(0, hDOTnegW), p); } // Recursively call this algorithm to compute the colorR seen by the reflected ray. double rgbReflected[] = rayTrace(s, r, num_reflections); rgb[0] *= rgbReflected[0];// * sphereColors[sphereSeen][0]; rgb[1] *= rgbReflected[1];// * sphereColors[sphereSeen][1]; rgb[2] *= rgbReflected[2];// * sphereColors[sphereSeen][2]; } return rgb; } int mx = 0, my = 0; double xAngle, yAngle; public boolean mouseDown(Event e, int x, int y) { mx = x; my = y; return true; } public boolean mouseDrag(Event e, int x, int y) { xAngle = .1 * (x - mx); yAngle = .1 * (y - my); //damage = true; mx = x; my = y; return true; } }