import java.util.*; import java.awt.*; public class Implicit extends PixApplet { //global variables go here double root[] = {0,0,0,0}; double point[] = {0,0,0}; double point2[] = {0,0,0}; double normal[] = {0,0,0}; double flector[]={0,0,0}; double light[]={1,1,1}; double rgb[] = {0,0,0}; double marshx=50; double marshy=250; boolean guess_reset; double carried_guess; boolean velvetflag; int textureflag; double zcenter=6; int shape=0; public void init() { super.init(); } public void setPix(int frame) { // SET PIXELS FOR THIS ANIMATION FRAME double v[] = {0,0,0}; double w[] = {0,0,0}; v[0] = 0; v[1] = 0; // CAMERA EYEPOINT IS AT THE ORIGIN v[2] = 0; double focalLength=1.0; guess_reset=true; int n = 0; 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; // COMPUTE RAY DIRECTION AT EACH PIXEL w[1] = (double)(H/2 - j) / W; w[2] = -focalLength; // PLACE IMAGE PLANE AT z = -focalLength rgb[0]=.4+.25*w[0]; rgb[1]=.4+.25*w[0]; rgb[2]=.5+.25*w[0]; rayTrace(v,w,0); // RAY TRACE AT THIS PIXEL //shadowTrace(v,w); pix[n++] = pack((int)(255.0 * rgb[0]), (int)(255.0 * rgb[1]), (int)(255.0 * rgb[2])); } guess_reset=true; } damage=true; } public void rayTrace(double v[], double w[], int depth) { double t=12345; //set the distance to the nearest sphere to a long way away. We use this distance later. t=minRoot(v,w); if (t<18){ //epsilon is used in determining the normal of arbitrary functions double ep=-.0018; double nx,ny,nz; double[] epx={w[0]+ep,w[1],w[2]}; double[] epy={w[0],w[1]+ep,w[2]}; double[] epz={w[0],w[1],w[2]+ep}; //double s[]= {v[0]+(t*w[0]), v[1]+(t*w[1]), v[2]+(t*w[2])}; //+(Math.random()%.0008); nx=minRoot(v,epx)-t; ny=minRoot(v,epy)-t; nz=minRoot(v,epz)-t; if (textureflag==1){//this creates the procedural bump mapping nx+=.003*Math.sin(w[1]*280); ny+=.003*Math.sin(w[1]*280); nz+=.003*Math.sin(w[1]*280); } if (velvetflag==true){//this creates the fuzzy velvet bump mapping nx+=(Math.random()%.0008); ny+=(Math.random()%.0008); nz+=(Math.random()%.0008); } double length=Math.sqrt(nx*nx+ny*ny+nz*nz); if (length<2){ double n[]={(nx/length),(ny/length),(nz/length)};//normalize the normals double eye[]={0,0,1};//really, this should be defined in terms of v double velvet=dotproduct(eye,n); double ndotlight=dotproduct(n,light); double dc=Math.max(0,-ndotlight); double dc2=Math.max(0,ndotlight); double h[]={2*ndotlight*n[0]-light[0], 2*ndotlight*n[1]-light[1], 2*ndotlight*n[2]-light[2]}; double sc=Math.pow(Math.max(dotproduct(eye,h),0),2); //double wn= (w[0]*n[0])+(w[1]*n[1])+(w[2]*n[2]); //double flector[]= {(-2*wn*n[0])+w[0], (-2*wn*n[1])+w[1], (-2*wn*n[2])+w[2]}; //double point[] = {s[0]+(.00001*flector[0]), s[1]+(.00001 * flector[1]), s[2]+(.00001 * flector[2])}; //shadowTrace(point,flector); //if (rgb[0]>.8) rayTrace(point, flector, depth + 1); if (velvetflag==true)//velvet shader { rgb[0]=Math.max(Math.min(.5*((1-velvet*velvet)*(2*dc+.5)+.5*sc),1),0); rgb[1]=Math.max(Math.min(.35*((1-velvet*velvet)*(2*dc+.5)+.5*sc),1),0); rgb[2]=Math.max(Math.min(.25*((1-velvet*velvet)*(2*dc+.5)+.5*sc),1),0); } else //phong shader { rgb[0]=Math.max(Math.min((.5*(dc)+.05*sc*sc-.2*dc2),1),0); rgb[1]=Math.max(Math.min(.30*((dc)+.15*sc*sc-.2*dc2),1),0); rgb[2]=Math.max(Math.min(.20*((dc)+.05*sc*sc-.2*dc2),1),0); } //rgb[0]=rgb[1]=rgb[2]=1-velvet*velvet; // rgb[0] =rgb[2]=rgb[1]=Math.pow((2-n[1]+n[0])/4,2); } } } public double surface (double a, double b, double c) {//returns what f(x,y,z) equals. double r1=1; double r2=.4; double x=a;//you can change the mapping from x,y,z to other coordinate systems here. double y=b; double z=c; double bx=x+5*marshx/W-2.5; double by=y-5.5*marshy/H+2.5; double cx=x+1.8; double cy=y+.5; double x2=x*x;double y2=y*y;double z2=z*z; double bx2=bx*bx;double by2=by*by; double cx2=cx*cx;double cy2=cy*cy; double r12plusr22=r1*r1+r2*r2; double r12minusr22=r1*r1-r2*r2; double k=(x2*x2+y2*y2+z2*z2 +2*x2*z2+2*x2*y2+2*y2*z2 -2*(r12plusr22)*x2+2*(r12plusr22)*z2-2*(r12plusr22)*y2 +(r12minusr22)*(r12minusr22));//equation for a torus switch (shape){ case 0: k=k* (bx2*bx2*bx2+by2+z2-1)-.3;//marshmellow equation. The .3 on the end is the blending constant. break; case 1: k=k* (bx2*bx2+by2*by2+z2*z2 +2*bx2*z2+2*bx2*by2+2*by2*z2 -2*(r12plusr22)*bx2+2*(r12plusr22)*z2-2*(r12plusr22)*by2 +(r12minusr22)*(r12minusr22))-.3;//torus equation break; case 2: k=k* (bx2+by2+z2-1)-.3;//sphere equation break; case 3: k=k* (bx2*bx2*bx2+by2*by2*by2+z2*z2*z2-1)-.5;//rounded cube equation break; case 4: k=Math.max(k,bx2+by2+z2-1); break; } //Math.max(Math.max(Math.max(Math.max(Math.max(x-1,y-1),z-1),-x+1),-y+1),-z+1); //Math.pow((2*z2+x2+y2-1),3)+.2*(z2*y2*y)+x2*y2*y; //x2+y2+z2-1; /* Math.min((Math.pow(x-1,4)+Math.pow(y,4)+Math.pow(z,4)-1)* ((x+.5-1)*(x+.5-1)+y3*(y+.5)+(z)*(z)-1)* ((x+.5-1)*(x+.5-1)+(y-1.5)*(y-1.5)+(z)*(z)-.25)-.1, Math.pow((2*z2+(x+1.5)*(x+1.5)+y2-1),3)+.2*(z2*y2*y)+(x+1.5)*(x+1.5)*y2*y); */ if (textureflag==2) k=k+.1*Math.sin(y*60);//one dimensional procedural volumetric texture if (textureflag==3) k=k+.1*Math.sin(y*60)+.1*Math.sin(x*60);//two dimensional procedural volumetric texture if (textureflag==4) k=k+.1*Math.sin(y*60)+.1*Math.sin(x*60)+.1*Math.sin(z*60);//three dimensional procedural volumetric texture if(Math.abs(marshx-250)+Math.abs(marshy-250)<75)//heart { double cubepart=(2*z2+cx2+cy2-1); k=k=Math.min(k,(cubepart*cubepart*cubepart+.2*(z2*cy2*cy)+cx2*cy2*cy)); } return k; } public double minRoot (double v[],double w[]){ double guess, front, back; double maxg=0.1;//the maximum "gradient" or amount the function can go up or down from one pixel to the next without losing track of the surface double accuracy=.00001;//how close the solver needs to get before it is okay double temp=18;//somewhere far past where all my objects are if (guess_reset==true){ guess=zcenter; front=guess; back=0; } else{ guess=carried_guess; front=guess+maxg; back=guess-maxg; } if (surface(v[0]-w[0]*front,v[1]-w[1]*front,v[2]-w[2]*front-zcenter)<0)//binary search for root { guess_reset=false; while(front-back>accuracy) { temp=(front+back)*.5; if (surface(v[0]-w[0]*temp,v[1]-w[1]*temp,v[2]-w[2]*temp-zcenter)<=0) front=temp; else back=temp; } } else guess_reset=true; if (guess_reset==true) temp=18; else carried_guess=temp; return temp; } public boolean mouseUp (Event e, int x, int y) { marshx=x; marshy=y; damage=true; return true; } public boolean keyUp(Event e, int key) { switch (key) { case 'v': if (velvetflag==false) velvetflag=true; else velvetflag=false; break; case 's': shape=(shape+1)%5; break; case 't': textureflag=(textureflag+1)%5; } damage=true; return true; } public double dotproduct(double a[], double b[]) { double product=(a[0]*b[0]+a[1]*b[1]+a[2]*b[2]); return product; } /* public void shadowTrace(double v[], double w[]){ double t[]=minRoot(v,w); if (t[1]<12345){ point[0] = v[0] + .999*t[1] * w[0]; point[1] = v[1] + .999*t[1] * w[1]; point[2] = v[2] + .999*t[1] * w[2]; double u[]=minRoot(point,light); if (u[1]<12345){ rgb[2]=rgb[1]=rgb[0]*=.5; //the shadow cuts out half of the light. It looks better that way. } } } */ }