import java.awt.*; import java.util.LinkedList; public class HW12b 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 oldTime; double dt; LinkedList surface_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_min = 2; 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; int alias = 0; double opacity = 0.0; Cube s; double dx[]; double dy[]; double dz[]; double old_dx[]; double old_dy[]; double old_dz[]; boolean frozen; Hemisphere white_left; Hemisphere white_right; Hemisphere shell_left; Hemisphere shell_right; Sphere yolk; Sphere white; Hemisphere bowl; Hemisphere water; int scene; boolean incrementScene; double start_time; double finish_time; double hard_time; boolean auto; boolean frame_timed; double timing[] = new double[] {1.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,10.0,10.0}; double vec[] = new double[3];//dummy displacement vector double disp_shell_left[][] = { {-3,-2,-10}, {-3,1,-10}, {0,1,0}, {-1,1,0}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30} }; double disp_shell_right[][] = { {-3,-2,-10}, {-3,1,-10}, {0,1,0}, {1,1,0}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30}, {-20,-2,-30} }; double disp_white_left[][] = { {-3,-2,-10}, {-3,1,-10}, {0,1,0}, {0,-0.6,0}, {0.35,-0.5,0}, {-0.25,-0.45,0}, {0.15,-0.35,0}, {0,1,0}, {-2,1,-2}, {-2,-2,-2} }; double disp_yolk[][] = { {-3,-2.1,-10}, {-3,0.9,-10}, {0,0.9,0}, {0,-0.6,0}, {0.35,-0.5,0}, {-0.25,-0.45,0}, {0.15,-0.35,0}, {0,1,0}, {-2,1,-2}, {-2,-2,-2} }; double disp_white_right[][] = { {-3,-2,-10}, {-3,1,-10}, {0,1,0}, {0,-0.6,0}, {0.35,-0.5,0}, {-0.25,-0.45,0}, {0.15,-0.35,0}, {0,1,0}, {2,1,2}, {2,-2,2} }; double white_opacity[] = { 0,0,0,0,0.2,0.4,0.8,1,1,1,1 }; double rotz_shell_left[] = { 0,0,0, -Math.PI/3, -Math.PI,-Math.PI,-Math.PI,-Math.PI,-Math.PI,-Math.PI }; double rotz_shell_right[] = { 0,0,0, Math.PI/3, Math.PI,Math.PI,Math.PI,Math.PI,Math.PI,Math.PI }; double rotx_cam[] ={ 0.06319689898685965, 0.06319689898685965, 0.06319689898685965, 0.06319689898685965, 0.06319689898685965, 0.35968470566939637, 0.35968470566939637, 1.5044025288211773, 0.06319689898685965, 0.06319689898685965 }; double roty_cam[] = { -0.5183627878423159, -0.5183627878423159, -0.5183627878423159, -0.5183627878423159, -0.5183627878423159, 0.7644542123735163, 0.7644542123735163, 0.5235987755982989, -0.5183627878423159, -0.5183627878423159 }; /** Do this once per run */ public void initialize() { camRotX = 0.06319689898685965; camRotY = -0.5183627878423159; camRotZ = 0.0; surface_list = new LinkedList(); // Hemisphere s = new Hemisphere(1.0); // Sphere s = new Sphere(1.0); // s = new Cube(1.0); // s.indigo().clear(); // surface_list.add(s); bowl = new Hemisphere(1.0); bowl.hollow().green(); water = new Hemisphere(0.9); water.blue(); surface_list.add(bowl); surface_list.add(water); shell_left = new Hemisphere(0.51); shell_left.hollow().pink(); shell_right = new Hemisphere(0.51); //flip it, instead of worrying about rotations shell_right.plane = new InfinitePlane(-1,0,0,0); shell_right.hollow().pink(); surface_list.add(shell_left); surface_list.add(shell_right); white = new Sphere(0.5); white.white().clear(); surface_list.add(white); white_left = new Hemisphere(0.5); white_left.white().clear(); white_right = new Hemisphere(0.5); white_right.white().clear(); white_right.plane = new InfinitePlane(-1,0,0,0); yolk = new Sphere(0.35); yolk.yellow(); surface_list.add(yolk); surface_list.add(new InfinitePlane(0,1,0,1).red()); /**dx = new double[] {0.0, 1.5*Math.cos(0/10), 0.0,0.0}; dy = new double[] {0.0, 0.0, 1.5*Math.cos(2*Math.PI/3+0/10), 0}; dz = new double[] {0.0,0.0,0.0, 1.5*Math.cos(2*2*Math.PI/3+0/10)}; */ frozen = false; scene = 0; start_time = 0; hard_time = 0; frame_timed = false; incrementScene = false; auto = false; } public void initFrame(double time){ //clear all matrices for(ImplicitSurface s: surface_list) s.matrix.identity(); bowl.matrix.rotateZ(Math.PI/2); bowl.matrix.translate(-0.2,0.0,0.0); water.matrix.rotateZ(Math.PI/2); water.matrix.translate(-0.3,0.0,0.0); } /** Overriden method that must define frame in doube pix[] array */ public void computeImage(double time){ dt = Math.min(1,time - oldTime); oldTime = time; //FREEZING NEEDS TO REVERT TO "LAST SEEN" NOT "LAST PAINTED" if(!frozen){ old_dx = dx; old_dy = dy; old_dz = dz; dx = new double[] {0.0, 1.5*Math.cos(time/10), 0.0,0.0}; dy = new double[] {0.0, 0.0, 1.5*Math.cos(2*Math.PI/3+time/10), 0}; dz = new double[] {0.0,0.0,0.0, 1.5*Math.cos(2*2*Math.PI/3+time/10)}; }else{ dx = old_dx; dy = old_dy; dz = old_dz; } // ANIMATION initFrame(time); hard_time += 1./4.;// second per frames double dt = time - start_time; if(frame_timed) dt = hard_time; if((incrementScene || auto) && (dt+start_time)>finish_time){ scene++; scene = scene % timing.length; if(scene==8){ surface_list.remove(white); white_left.setOpacity(white.opacity); white_right.setOpacity(white.opacity); surface_list.add(white_left); surface_list.add(white_right); } if(scene==0){ surface_list.add(white); surface_list.remove(white_left); surface_list.remove(white_right); } System.out.println(scene); start_time = time; hard_time = 0; dt = 0; incrementScene = false; } water.matrix.rotateX(0.01*Math.sin(time/10)); water.matrix.rotateY(0.01*Math.sin(2*Math.PI/3+time/6)); finish_time = start_time + timing[scene]; double f = Math.min(1.0,(dt)/(finish_time-start_time)); int last_scene = scene==0 ? scene : scene-1; //bring egg pu above table lerp(f, disp_shell_left[last_scene], disp_shell_left[scene], vec); shell_left.matrix.translate(vec[0],vec[1],vec[2]); shell_left.matrix.rotateZ(lerp(f, rotz_shell_left[last_scene], rotz_shell_left[scene])); lerp(f, disp_shell_right[last_scene], disp_shell_right[scene], vec); shell_right.matrix.translate(vec[0],vec[1],vec[2]); shell_right.matrix.rotateZ(lerp(f, rotz_shell_right[last_scene], rotz_shell_right[scene])); lerp(f, disp_yolk[last_scene], disp_yolk[scene], vec); yolk.matrix.translate(vec[0],vec[1],vec[2]); if(scene<8){ white.setOpacity(lerp(f,white_opacity[last_scene],white_opacity[scene])); lerp(f, disp_white_left[last_scene], disp_white_left[scene], vec); white.matrix.translate(vec[0],vec[1],vec[2]); }else{ lerp(f, disp_white_left[last_scene], disp_white_left[scene], vec); white_left.matrix.translate(vec[0],vec[1],vec[2]); lerp(f, disp_white_right[last_scene], disp_white_right[scene], vec); white_right.matrix.translate(vec[0],vec[1],vec[2]); } camRotX = lerp(f, rotx_cam[last_scene], rotx_cam[scene]); camRotY = lerp(f, roty_cam[last_scene], roty_cam[scene]); // MOVE THE WORLD ACCORDING TO "CAMERA" Matrix4x4 m = new Matrix4x4(); m.identity(); m.translate(0,0,-20); m.rotateX(camRotX); m.rotateY(camRotY); m.rotateZ(camRotZ); int in= 0; for(ImplicitSurface s: surface_list){ Matrix4x4 ms = m.getCopy(); ms.postMultiply(s.matrix); s.transform(ms); in++; } // SLOWLY INCREASE RESOLUTION if(pass_pixel_width > pixel_width_min) pixel_width = pass_pixel_width; pass_pixel_width = -1; if(mouseDown) pixel_width = pixel_width_max; else pixel_width = Math.max(pixel_width_min, pixel_width/2); double rgb[] = new double[3]; double v [] = new double[3]; double w [] = new double[3]; // NON ANTI-ALIASING IS FASTER BECAUSE IT DOESN'T LOOP OVER DOUBLES if(alias==0){ 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 // CONSTRUCT ORIGINAL RAY FROM EYE 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 //initialize pixel color to black rgb[0]=0; rgb[1]=0; rgb[2]=0; normalize(w); rayTrace(v, w, rgb, 0, null , false); // COMPUTE COLOR AT PIXEL BY RAY TRACING int[] color = new int[] {(int)(255*rgb[0]), (int)(255*rgb[1]), (int)(255*rgb[2]) }; for(int _i = i; _i0&&tn0){ useLight[k] = false; break; } } } } } type = surface.type; ambientColor = surface.ambientColor; diffuseColor = surface.diffuseColor; specularColor = surface.specularColor; specularPower= surface.specularPower; if(!skip && surface.opacity>0) phong(normal, rgb, depth, surface.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] }; // air is 1, air is not being in something double n1 = null==within ? 1.0 : within.n; double n2 = (null!=within && surface==within) ? 1.0 : surface.n; double theta1 = Math.acos(norm(c)); double theta2 = Math.asin(Math.sin(theta1)*n1/n2); normalize(c); normalize(s); wp[0] = c[0]*Math.cos(theta2)+s[0]*Math.sin(theta2); wp[1] = c[1]*Math.cos(theta2)+s[1]*Math.sin(theta2); wp[2] = c[2]*Math.cos(theta2)+s[2]*Math.sin(theta2); //normalize(wp); vp[0] = point[0] + EPSILON*wp[0]; vp[1] = point[1] + EPSILON*wp[1]; vp[2] = point[2] + EPSILON*wp[2]; //if(theta10){ 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); } } // MERGE SORT /** 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;j0.5){ opacity = 0.0; } pass_pixel_width = pixel_width_max; break; case '-': case '_': pixel_width_max= Math.max(1,pixel_width_max/2); pixel_width_min= Math.max(1,pixel_width_min/2); pass_pixel_width = pixel_width_max; break; case '+': case '=': pixel_width_max*=2; pixel_width_min*=2; pass_pixel_width = pixel_width_max; break; case 't': case 'T': texture = !texture; if(texture){ shell_left.type = 3; shell_right.type = 3; yolk.type = 1; } pass_pixel_width = pixel_width_max; break; case 'f': case 'F': frame_timed = !frame_timed; break; case 'a': case 'A': auto = !auto; break; case 'c': case 'C': System.out.println("camRotX: "+camRotX); System.out.println("camRotY: "+camRotY); System.out.println("camRotZ: "+camRotZ); break; } return true; } public boolean mouseDown(Event e, int x, int y){ mouseDown = true; return true; } public boolean mouseUp(Event e, int x, int y){ mouseDown = false; return true; } public boolean mouseDrag(Event e, int x, int y) { int wx = W/2; int wy = H/2; y = H - y - 1; double radius = Math.min( W, H )/2.; double sx = (x-wx)/radius; double sy = (y-wy)/radius; if(sx*sx+sy*sy>1){ 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})); roty_cam[scene] = Math.PI*(x-W/2)/W; //camRotX = -Math.PI*(y-H/2)/H; rotx_cam[scene] = 0.02-0.25*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; } // USEFUL MATH public static void lerp(double f , double y0[], double y1[], double dst[]){ for(int i=0;i3){ 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