import java.awt.*; import java.util.LinkedList; class Sphere extends ImplicitSurface{ public Sphere(double x, double y, double z, double r){ o= new double[] {x,y,z}; c= new double[] {x,y,z}; this.r = r; v= new double[] {0,0,0}; f= new double[] {0,0,0}; m=1; contact_normal = null; } } class Cube extends ImplicitSurface{ double plane[][] = { {-1, 0, 0,-1}, { 1, 0, 0,-1}, { 0,-1, 0,-1}, { 0, 1, 0,-1}, { 0, 0,-1,-21}, { 0, 0, 1,-19} }; public Cube(double x, double y, double z, double r){ o= new double[] {x,y,z}; c= new double[] {x,y,z}; this.r = r; v= new double[] {0,0,0}; f= new double[] {0,0,0}; m=1; } } class ImplicitSurface{ double c[];//transformed center double o[];//center double v[]; double f[]; double r;//radius double n = 1.1;//water double m; int type; double ambientColor[] = { 0.2, 0.2, 0.2 }; double diffuseColor[] = { 0.8, 0.8, 0.8 }; double specularColor[] = {.1,.1,.1}; double specularPower = 3.0; double contact_normal[]; double opacity = 1.0; public ImplicitSurface metal(){ specularColor[0] = diffuseColor[0]; specularColor[1] = diffuseColor[1]; specularColor[2] = diffuseColor[2]; HW11.normalize(specularColor); specularColor[0] *= 0.51; specularColor[1] *= 0.51; specularColor[2] *= 0.51; return this; } public ImplicitSurface blue(){ ambientColor = new double[] {0.1,0.1,0.2}; diffuseColor = new double[] {0.1,0.1,0.8}; return this; } public ImplicitSurface green(){ ambientColor = new double[] {0.1,0.2,0.1}; diffuseColor = new double[] {0.1,0.8,0.1}; return this; } public ImplicitSurface pink(){ ambientColor = new double[] {0.2,0.1,0.1}; diffuseColor = new double[] {0.6,0.0,0.3}; return this; } public ImplicitSurface purple(){ ambientColor = new double[] {0.12,0.1,0.18}; diffuseColor = new double[] {0.3,0.0,0.6}; return this; } public ImplicitSurface indigo(){ ambientColor = new double[] {0.1,0.12,0.18}; diffuseColor = new double[] {0.0,0.3,0.6}; return this; } public ImplicitSurface yellow(){ ambientColor = new double[] {0.18,0.12,0.1}; diffuseColor = new double[] {0.4,0.4,0.0}; return this; } public ImplicitSurface clear(){ opacity = 0.0; return this; } public ImplicitSurface semiclear(){ opacity = 0.5; return this; } public ImplicitSurface red(){ ambientColor = new double[] {0.2,0.1,0.1}; diffuseColor = new double[] {0.8,0.1,0.1}; return this; } public void applyForce(double dt){ v[0] += f[0]/m*dt; v[1] += f[1]/m*dt; v[2] += f[2]/m*dt; } public void applyVelocity(double dt){ c[0] += v[0]*dt; c[1] += v[1]*dt; c[2] += v[2]*dt; } public void addToForce(double x, double y, double z){ f[0]+=x; f[1]+=y; f[2]+=z; } } public class HW11 extends MISApplet{ // MATERIAL PROPERTIES double ambientColor[] = { 0.2, 0., 0.0 }; double diffuseColor[] = { 0.8, 0.0, 0.0 }; double specularColor[] = {.1,.1,.1}; double specularPower = 20.0; // LIGHTING PROPERTIES /** double lightDirections[][] = { {1,0,0} }; double lightColors[][] = { {1.0,1.0,1.0} }; */ double lightDirections[][] = { {1,1,1}, {0,0,1} ,{-1,0,-1} }; double lightColors[][] = { {0.8,0.9,0.8}, {0.5,0.6,0.5}, {0.5,0.6,0.5} }; boolean useLight[] = new boolean[lightDirections.length]; int res = 10; double foc = 5.0;//focal length of pinhole camera int type; double vec1[] = new double[3];//temporary 3D vector double vec2[] = new double[3];//temporary 3D vector double oldTime; double dt; LinkedList sphere_list; LinkedList cube_list; final double EPSILON = 0.0001; final double INFINITY = Double.MAX_VALUE; double eye[]; boolean mouseDown = false; boolean texture = false; int pixel_width_max = 10; int pixel_width = pixel_width_max; int pass_pixel_width = pixel_width; double camRotX; double camRotY; double camRotZ; double rotY; double lrotY; double rrotY; double lrotX; double rrotX; double xMAX=2; double yMAX=2; double zMAX=2; /** Do this once per run */ public void initialize() { cube_list = new LinkedList(); sphere_list = new LinkedList(); Cube c = new Cube(0,0,2,0.5); c.yellow().metal(); //cube_list.add(c); Sphere s = new Sphere(0,0,0,0.5); s.indigo().metal(); s.type = 2; sphere_list.add(s); Sphere u = new Sphere(0,0,1,0.25); u.purple().metal(); u.type = 1; sphere_list.add(u); Sphere t = new Sphere(0,1.0,-1.5,1.0); t.yellow().semiclear().metal(); t.type = 0; sphere_list.add(t); } /** Overriden method that must define frame in doube pix[] array */ public void computeImage(double time){ dt = Math.min(1,time - oldTime); oldTime = time; Matrix4x4 m = new Matrix4x4(); m.identity(); m.translate(0,0,-20); m.rotateX(camRotX); m.rotateY(camRotY); m.rotateZ(camRotZ); for(Sphere s: sphere_list){ m.translate(s.o[0],s.o[1],s.o[2]); m.transform(0,0,0,s.c); m.translate(-s.o[0],-s.o[1],-s.o[2]); } for(Cube s: cube_list){ m.translate(s.o[0],s.o[1],s.o[2]); m.transform(0,0,0,s.c); m.translate(-s.o[0],-s.o[1],-s.o[2]); } double rgb[]; int color[]; double v [] = new double[3]; double w [] = new double[3]; if(pass_pixel_width > 1) pixel_width = pass_pixel_width; pass_pixel_width = -1; if(mouseDown) pixel_width = pixel_width_max; else pixel_width = Math.max(1, pixel_width/2); for (int i = 0 ; i < W ; i+=pixel_width) // LOOP OVER IMAGE COLUMNS for (int j = 0 ; j < H ; j+=pixel_width) { // LOOP OVER IMAGE ROWS //System.out.println("ij "+i+" , "+j); v[0] = 0; v[1] = 0; v[2] = 0; w[0] = (double)(i - W/2) / W; // COMPUTE RAY DIRECTION AT EACH PIXEL w[1] = (double)(H/2 - j) / W; // w[2] = -foc; // PLACE IMAGE PLANE AT z=-focalLength rgb = new double[] {0,0,0};//initialize pixel color to black normalize(w); rayTrace(v, w, rgb, 0, null , false); // COMPUTE COLOR AT PIXEL BY RAY TRACING color = new int[] {(int)(255*rgb[0]), (int)(255*rgb[1]), (int)(255*rgb[2]) }; for(int _i = i; _i1){ double denom = Math.sqrt(sx*sx+sy*sy); sx /= denom; sy /= denom; } double sz= Math.sqrt(Math.max(0,1-sx*sx-sy*sy)); double off[] = {sx,sy,sz}; normalize(off); //camRotX = sign(sy)*Math.acos(dot(off,new double[]{1,0,0})); //camRotY = sign(sx)*Math.acos(dot(off,new double[]{0,0,1})); //camRotZ = Math.acos(dot(off,new double[]{1,0,0})); camRotY = Math.PI*(x-W/2)/W; camRotX = -Math.PI*(y-H/2)/H; //camRotY = sign(x)*Math.PI/2*(x-W/2)/W; //camRotX = -sign(y)*Math.PI/2*(y-H/2)/H; return true; } private void rayTrace(double v[], double w[], double rgb[], int depth, ImplicitSurface within, boolean skip){ double t = INFINITY; ImplicitSurface closest_sphere = null; //loop over all spheres for(Sphere sphere: sphere_list){ //Try to intersect the ray with Sphn, to get tn; //If the ray intersects Sphn at some tn, and tn < t, then this is the //nearest sphere, so: double tn = raySphereIntersect(v,w,sphere, skip); //System.out.println("tn: "+tn ); if(tn>0&&tn0&&tn0){ useLight[k] = false; break; } } for(Cube cube: cube_list){ double tn = rayCubeIntersect(v,w,cube); if(tn>0&&tn0) phong(normal, rgb, depth, sphere.opacity); // REFLECTION double vp[] = new double[] { point[0] + EPSILON*normal[0], point[1] + EPSILON*normal[1], point[2] + EPSILON*normal[2] }; normalize(normal);//superfluous? double wdn = dot(w,normal);// w dot normal double wp[] = new double[] { w[0] - 2*wdn*normal[0], w[1] - 2*wdn*normal[1], w[2] - 2*wdn*normal[2] }; //recursive call to continue ray rayTrace(vp,wp,rgb, depth+1,within, false); //REFRACTION double c[] = new double[] { wdn*normal[0], wdn*normal[1], wdn*normal[2] }; double s[] = new double[] { w[0]-c[0], w[1]-c[1], w[2]-c[2] }; double n1 = null==within ? 1.0 : within.n; double n2 = (null!=within && sphere==within) ? 1.0 : sphere.n; double theta1 = Math.acos(norm(c)); double theta2 = Math.asin(Math.sin(theta1)*n1/n2); normalize(c); normalize(s); wp = new double[]{ c[0]*Math.cos(theta2)+s[0]*Math.sin(theta2), c[1]*Math.cos(theta2)+s[1]*Math.sin(theta2), c[2]*Math.cos(theta2)+s[2]*Math.sin(theta2) }; //normalize(wp); vp = new double[] { point[0] + EPSILON*wp[0], point[1] + EPSILON*wp[1], point[2] + EPSILON*wp[2] }; //if(theta1tl){ tl = t; c.contact_normal = normal; } }else{ //EXITTING if(t=0){ tn = (-B-Math.sqrt(B*B-4*A*C))/2*A; if(skip && tn<=0){ // System.out.println("INSIDE"); tn = (-B+Math.sqrt(B*B-4*A*C))/2*A; } } return tn; } /** Phong algorithm for shading vertices * takes a normal and an initial color then adds phong shade to color given */ private void phong(double[] normal, double rgb[], int depth, double opacity){ // SAMPLE THE FUNCTION FOUR TIMES TO GET GRADIENT INFO if(texture){ double f0 = f(normal[0] ,normal[1] ,normal[2] ), fx = f(normal[0]+.0001,normal[1] ,normal[2] ), fy = f(normal[0] ,normal[1]+.0001,normal[2] ), fz = f(normal[0] ,normal[1] ,normal[2]+.0001); // SUBTRACT THE FUNCTION'S GRADIENT FROM THE SURFACE NORMAL normal[0] -= (fx - f0) / .0001; normal[1] -= (fy - f0) / .0001; normal[2] -= (fz - f0) / .0001; normalize(normal); } // ADD AMBIENT for (int i = 0 ; i < 3 ; i++){ color[i] = 0; if(depth==0) color[i] = ambientColor[i]; } // LOOP THROUGH ALL LIGHT SOURCES for (int l = 0 ; l < lightDirections.length ; l++) { if(useLight[l]){ // COMPUTE DIFFUSE double n_dot_l = dot(lightDirections[l], normal); if (n_dot_l > 0) for (int i = 0 ; i < 3 ; i++) diffuse[i] = diffuseColor[i] * n_dot_l; else for (int i = 0 ; i < 3 ; i++) diffuse[i] = 0; // COMPUTE REFLECTION DIRECTION for (int i = 0 ; i < 3 ; i++) reflection[i] = 2 * n_dot_l * normal[i] - lightDirections[l][i]; double r_dot_e = dot(reflection, eyeDirection); // COMPUTE SPECULAR if (r_dot_e > 0) { double specularFactor = Math.pow(r_dot_e, specularPower); for (int i = 0 ; i < 3 ; i++) specular[i] = specularColor[i] * specularFactor; } else for (int i = 0 ; i < 3 ; i++) specular[i] = 0; // ADD DIFFUSE AND SPECULAR for (int i = 0 ; i < 3 ; i++) color[i] += lightColors[l][i] * (diffuse[i] + specular[i]); } } // SEND COLOR TO THE FRAME BUFFER for (int i = 0 ; i < 3 ; i++) { rgb[i] += opacity*Math.pow(0.25,depth)* Math.max(0, Math.min(1.0, color[i])); rgb[i] = Math.min(rgb[i],1.0); } } /** Universal Merge sort using dist function and point P */ private double[][] mergeSort(double[][] m){ //sub-problem arrays and result double[][] left, right, result; //base cases if(m.length <= 1) return m; //find middle and create and fille sub-problem arrays int middle = m.length/2; left = new double[middle][6]; right = new double[m.length-middle][6]; for(int j=0;j3){ if(equals(p1,p2)){ p1 = triangle[3]; }else if(equals(p1,p3)){ p1 = triangle[3]; }else if(equals(p2,p3)){ p3 = triangle[3]; } } double[] a = new double[] {p2[0]-p1[0],p2[1]-p1[1],p2[2]-p1[2]}; double[] b = new double[] {p3[0]-p1[0],p3[1]-p1[1],p3[2]-p1[2]}; /* Surface normal is (p2-p1)x(p3-p1) * of which we only need the z-coordinate * so if n = axb then the z-coordinate is * a1b2 - a2b1 * which in this case is * (p21 - p11)(p32-p12)-(p32-p12)(p31-p11) */ //let's try that again //(a2b3 - a3b2, a3b1 - a1b3, a1b2 - a2b1) double[] cross = new double[] {a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0]}; return cross[2]; } public static boolean equals(double p1[], double p2[]){ for(int i =0; i