//RTLab.java --Derek Morris -4/1/08 /* This is a simple raytracer written in Java. */ import java.awt.*; import java.lang.*; import java.math.*; import java.io.*; import javax.swing.*; import java.util.*; public class RTLab extends JFrame { static final int xSize = 512, ySize = 384; //screen size, mults of 32 static final int blocksize = 32, bigblocksize = blocksize + 1; static final int xBlocks = xSize / blocksize, yBlocks = ySize / blocksize; //32 x 32 blocks primitive[] objectlist = new primitive[70]; //the array of primitives int[] lightindicies = new int[10]; //the array that stores the indicies of the lights int primnumber = 5, numblights = 2; //array maxes final int maxlevel = 100; //maximum recursion level double lastdist; final double xlow = -4, xhigh = 4, ylow = 3, yhigh = -3; //size of the assumed screen plane final double deltax = (xhigh - xlow) / xSize, deltay = (yhigh - ylow) / ySize; //change for each pixel ray e = new ray(); /* Assignment: 1. Understand the code. This will help for the later parts. 2. Add some more spheres. 3. Add spheres in an array in the background. 4. Add shift code and animate. 5. Add rotate code and animate. 6. Add support for file I/0 (for the scenes). 7. Add support for triangles. 8. Make a cube out of 12 triangles, 2 per face. 9. Allow file importation of trimeshes in the .obj format 10. Add an HUD that is added as the scene is rendering. //for the following use very simple screens so that it is easier to see. 11. Allow movement of the camera with the keyboard. 12. Add gravity. 13. Add sphere-to-sphere collisions.(think unit 2, lab 20...but better) THIS MUST BE DONE IN JAVA!!!!!!! BTW, do not ask me for help every two seconds. */ public static void main (String args[]) { RTLab frame = new RTLab(); frame.setBackground(Color.black); frame.setForeground(Color.green); frame.setSize(xSize,ySize); frame.setTitle("Raytrace Lab"); frame.setFont(new Font("SansSerif", Font.PLAIN, 20)); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } public void intitializeScene() { //light 1 objectlist[0] = new sphere( 0.0, 5.0, 5.0, true, 0.5, 1.0, 0.0, 0.0, 0.6, 0.6, 0.6, 0.1); lightindicies[0] = 0; numblights = 1; //light 2 objectlist[1] = new sphere(2.0, 5.0, -5.0, true, 0.5, 1.0, 0.0, 0.0, 0.7, 0.7, 0.8, 0.1); lightindicies[1] = 1; numblights = 2; //ground plane objectlist[2] = new plane( 0.0, 1.0, 0.0, false, 0.6, 1.0, 0.0, 0.0, 0.6, 0.2, 0.2, 4.4); //sphere 1 objectlist[3] = new sphere( 4, -1, 3, false, 0.2, 0.0, 1.0, 0.8, 1.0, 0.2, 0.2, 2.0); //sphere 2 objectlist[4] = new sphere( -5.5, 2.5, 4.0, false, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 2.0); //(x, y, z, light,refl,diff,spec,refrac,red,green,blue,radius) //extra sphere objectlist[5] = new sphere( 1.5, 3.8, 1.0, false, 0.9, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.5); //(x, y, z, light,refl,diff,spec,refrac,red,green,blue,radius) //back plane objectlist[6] = new plane( 0.4, 0.0, -1.0, false, 0.0, 0.6, 0.0, 0.0, 0.5, 0.3, 0.5, 12.0); //ceiling plane objectlist[7] = new plane( 0.0, -1.0, 0.0, false, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.7, 7.4); //total # of primitives primnumber = 8; return; } public Color raytrace(ray r, int level) { Color returnme = new Color(0,0,0); //this gets returned at the end if( level > maxlevel) //if max recursion level is hit (worry about it later) { //System.out.println("over 9000!!"); //use this for debugging return returnme; } boolean oldclosest = true; double thisone = 0.0; //the distance to this one (this object, if you prefer) double closestdist = 200.0; //max draw distance for every ray primitive closestobject = null; //set the value of the "closest object" to null double retred = 0.0, retgreen = 0.0, retblue = 0.0; //rgb for final color set. these should go between 0 and 1 vector pointofinter; //vector for the point at which the ray intersects the primitive //nearest intersection finding code for ( int a = 0; a < primnumber; a++) //for every primitive { thisone = objectlist[a].intersect( r, closestdist ); //check for intersection if(thisone < closestdist && thisone > 0.0) {//if it is closer than the last one and it hit ( a miss is 0) closestdist = thisone; //set the new closest distance closestobject = objectlist[a]; //set the "closest object" index to the new hit oldclosest = false; } } //if no hit, return black if(oldclosest) //if we hit nothing, return black return returnme; //return //point of intersection pointofinter = new vector(r.getOrig().add( (r.getDir().scale(closestdist)))); //normal at the point of intersect vector t = new vector(closestobject.getnormal(pointofinter)); //if it's a light, return the correct color if(closestobject.isalight) { vector tolight = new vector(closestobject.myloc.subtract(r.getOrig())); tolight.normalize(); double rate = Math.pow((1.0 - Math.acos(tolight.dotproduct(r.getDir()))/90.0), 21000); retred = (closestobject.myred * rate); retgreen = (closestobject.mygreen * rate); retblue = (closestobject.myblue * rate); // retred %= 1.0; // retgreen %= 1.0; // retblue %= 1.0; if(retred > 1.0)retred=1.0; if(retgreen > 1.0)retgreen=1.0; if(retblue > 1.0)retblue=1.0; if(retred < 0.0)retred=0.0; if(retgreen < 0.0)retgreen=0.0; if(retblue < 0.0)retblue=0.0; returnme = new Color((float)retred, (float)retgreen, (float)retblue);//the lights are shown as the correct color return returnme; //return } else //If it isn't a light { //shading for (int b = 0; b < numblights; b++) //for each light { //point of intersect to light vector k = new vector(objectlist[b].getlocation().subtract(pointofinter)); double lightdistance = k.length(); k.normalize(); double dot; //check if lit by this light double lit = 1.0; //comment this block to turn off shadows { ray l = new ray(pointofinter.add(k.scale(0.00001)),k); for(int d = 0; d < primnumber; d++) { if(objectlist[d].isalight) continue; double v = objectlist[d].intersect(l,lightdistance); if((v <= lightdistance) && (v > 0.0)) { lit = 0.0; break; } } } //calculate diffuse shading if((closestobject.diffusivity > 0.0) && (lit > 0.0) ) { dot = k.dotproduct(t); if (dot > 0) { double diff = dot * closestobject.diffusivity; //add the diffusivity times the material color times the light color retred += diff * closestobject.myred * objectlist[b].myred; retgreen += diff * closestobject.mygreen * objectlist[b].mygreen; retblue += diff * closestobject.myblue * objectlist[b].myblue; } } else //otherwise return black { returnme = Color.black; } if((closestobject.specularity > 0.0) && (lit > 0.0)) { vector reflectionoflightvector = new vector( k.subtract(t.scale(k.dotproduct(t)).scale(2.0)) ); dot = r.getDir().dotproduct(reflectionoflightvector); if(dot > 0) { double pointspec = Math.pow(dot,20) * closestobject.specularity; retred += pointspec * objectlist[b].myred; retgreen += pointspec * objectlist[b].mygreen; retblue += pointspec * objectlist[b].myblue; } } } //now relfect if(closestobject.reflectivity > 0.0) { vector reflectiondirection = new vector( r.getDir().subtract( t.scale(r.getDir().dotproduct(t)).scale(2.0) ) ); ray e = new ray( pointofinter.add(reflectiondirection.scale(0.00001)), reflectiondirection); Color w = raytrace(e, level + 1); retred += closestobject.reflectivity * (w.getRed() / 255.0) * closestobject.myred; retgreen += closestobject.reflectivity * (w.getGreen() / 255.0) * closestobject.mygreen; retblue += closestobject.reflectivity * (w.getBlue() / 255.0) * closestobject.myblue; } //now refract if(closestobject.refractivity > 0) { double p = 1.0/1.3;//objectlist[closestobject].refracind; vector u = t.scale(closestdist/Math.abs(closestdist)); double c1 = -u.dotproduct(r.getDir()); double c2 = 1.0 - p * p * (1.0 - c1 * c1); if(c2 > 0.0) { vector b = new vector(r.getDir().scale(p).add(u.scale(p * c1 - Math.sqrt(c2)))); ray y = new ray( pointofinter.add(b.scale(0.00001)).scale(-1.0), b); Color wow = raytrace(y, level + 1); double f = 0.15 * lastdist; retred += (wow.getRed() / 255.0) * closestobject.myred * f; retgreen += (wow.getGreen() / 255.0) * closestobject.mygreen * f; retblue += (wow.getBlue() / 255.0) * closestobject.myblue * f; } } } // retred %= 1.0; // retgreen %= 1.0; // retblue %= 1.0; if(retred > 1.0)retred=1.0; if(retgreen > 1.0)retgreen=1.0; if(retblue > 1.0)retblue=1.0; if(retred < 0.0)retred=0.0; if(retgreen < 0.0)retgreen=0.0; if(retblue < 0.0)retblue=0.0; returnme = new Color( (float)retred, (float)retgreen, (float)retblue); lastdist = closestdist; return returnme;//convert to a color ^ //return } public void render(Graphics2D g) { double xpos = xlow, ypos = ylow; vector origin = new vector ( 0, 0, -5); double origscreendistance = 0 - origin.getz(); vector direction; ray raytobesent = new ray(); while(true){ long start = System.currentTimeMillis(); //start time for (int blockY = 0; blockY < yBlocks; blockY++) for (int blockX = 0; blockX < xBlocks; blockX++)//for each block { ypos = ylow + (deltay * (blockY * blocksize)); for (int y = 1 + (blockY*blocksize); y < (blockY*blocksize) + bigblocksize; y++) //for every scanline { xpos = xlow + (deltax * (blockX * blocksize)); //reset the xposition for (int x = 1 + (blockX*blocksize); x < bigblocksize + (blockX*blocksize); x++) //for every pixel on the scanline { //direction vector for the pixel direction = new vector(xpos - origin.getx(), ypos - origin.gety(), origscreendistance); direction.normalize(); //normalize it raytobesent.Orig = origin; raytobesent.Dir = direction; //make it a ray g.setColor(raytrace(raytobesent,1)); //set the color to (raytrace)!!! g.drawLine(x,y,x,y); //draw a pixel with the color xpos += deltax; //shift x } ypos += deltay; //shift y } } //end time System.out.println((double)(System.currentTimeMillis() - start) / 1000.0); } } public void paint (Graphics g1) { Graphics2D g = (Graphics2D) g1; g.clearRect(0,0,getWidth(),getHeight()); intitializeScene(); render(g); } } abstract class primitive { //Variables public boolean isalight; public double reflectivity, diffusivity, specularity, refractivity; public double myred, mygreen, myblue; public vector myloc; //Constructor Methods public primitive() { isalight = false; reflectivity = 0.5; diffusivity = 1.0; specularity = 1.0; refractivity = 1.0; myred = 0.5; mygreen = 0.5; myblue = 0.5; } public primitive(boolean newlight, double newrefl, double newdiff, double newspec, double newrefrac, double newred, double newgreen, double newblue) { isalight = newlight; reflectivity = newrefl; diffusivity = newdiff; specularity = newspec; refractivity = newrefrac; myred = newred; mygreen = newgreen; myblue = newblue; } public primitive(primitive newprim) { isalight = newprim.getlight(); reflectivity = newprim.getRefl(); diffusivity = newprim.getDiff(); specularity = newprim.getSpec(); refractivity = newprim.getRefrac(); myred = newprim.myred; mygreen = newprim.mygreen; myblue = newprim.myblue; } //Modifier Methods public void setlight(boolean newlight) { isalight = newlight; } public void setRefl(double newrefl) { reflectivity = newrefl; } public void setDiff(double newdiff) { diffusivity = newdiff; } public void setSpec(double newspec) { specularity = newspec; } public void setRefrac(double newrefrac) { refractivity = newrefrac; } public void setColor(double newred, double newgreen, double newblue) { myred = newred; mygreen = newgreen; myblue = newblue; } //Acsessor Methods public boolean getlight() { return isalight; } public double getRefl() { return reflectivity; } public double getDiff() { return diffusivity; } public double getSpec() { return specularity; } public double getRefrac() { return refractivity; } public abstract double intersect(ray r, double dist); public abstract vector getnormal(vector b); public abstract vector getlocation(); } class vector { //Variables public double x, y, z; //Constructor Methods public vector() { x = (double)0.0; y = (double)0.0; z = (double)0.0; } public vector(double nx, double ny, double nz) { x = nx; y = ny; z = nz; } public vector(vector b) { x = b.x; y = b.y; z = b.z; } //Modifier Methods public void set(double nx, double ny, double nz) { x = nx; y = ny; z = nz; } public void normalize() { double L = 1.0 / length(); x *= L; y *= L; z *= L; } //Acsessor Methods public double length() { return (Math.sqrt((x*x)+(y*y)+(z*z))); } public double lengthSquared() { return ((x*x)+(y*y)+(z*z)); } public double getx() { return x; } public double gety() { return y; } public double getz() { return z; } public void setadd(vector b) { x += b.x; y += b.y; z += b.z; } public void setsubtract(vector b) { x -= b.x; y -= b.y; z -= b.z; } public void setscale(double d) { x *= d; y *= d; z *= d; } public void setmultiply(vector b) { x *= b.x; y *= b.y; z *= b.z; } //Operations public double dotproduct(vector b) { return ((x*b.x)+(y*b.y)+(z*b.z)); } public vector crossproduct(vector b) { vector g = new vector( ((y * b.z) - (z * b.y)), ((z * b.x) - (x * b.z)), ((x * b.y) - (y * b.x)) ); return g; } public vector add(vector b) { vector g = new vector( (x + b.x), (y + b.y), (z + b.z) ); return g; } public vector subtract(vector b) { vector g = new vector( (x - b.x), (y - b.y), (z - b.z) ); return g; } public vector scale(double d) { vector g = new vector( (x * d), (y * d), (z * d) ); return g; } public vector multiply(vector b) { vector g = new vector( (x * b.x), (y * b.y), (z * b.z) ); return g; } } class ray { //Variables public vector Orig, Dir; //Constructor Methods public ray() { Orig = new vector(0.0, 0.0, 0.0); Dir = new vector(0.0, 0.0, 0.0); } public ray(vector newOrig, vector newDir) { Orig = new vector(newOrig); Dir = new vector(newDir); } public ray(ray b) { Orig = new vector(b.Orig); Dir = new vector(b.Dir); } //Modifier Methods public void setOrig(vector newOrig) { Orig = new vector(newOrig); } public void setDir(vector newDir) { Dir = new vector(newDir); } //Acsessor Methods public vector getOrig() { return Orig; } public vector getDir() { return Dir; } } class sphere extends primitive { //Variables public double radius, radiussqrd, recipradius; //Constructor Methods public sphere() { isalight = false; reflectivity = 0.5; diffusivity = 1.0; specularity = 1.0; refractivity = 1.0; myred = 0.5; mygreen = 0.5; myblue = 0.5; radius = 1.0; radiussqrd = 1.0; recipradius = 1.0; myloc = new vector(0.0,0.0,0.0); } public sphere(double x, double y, double z, boolean newlight, double newrefl, double newdiff, double newspec, double newrefrac, double newred, double newgreen, double newblue, double newradius) { isalight = newlight; reflectivity = newrefl; diffusivity = newdiff; specularity = newspec; refractivity = newrefrac; myred = newred; mygreen = newgreen; myblue = newblue; radius = newradius; radiussqrd = radius * radius; recipradius = 1.0 / radius; myloc = new vector(x, y, z); } public sphere(vector q, boolean newlight, double newrefl, double newdiff, double newspec, double newrefrac, double newred, double newgreen, double newblue, double newradius) { isalight = newlight; reflectivity = newrefl; diffusivity = newdiff; specularity = newspec; refractivity = newrefrac; myred = newred; mygreen = newgreen; myblue = newblue; radius = newradius; radiussqrd = radius * radius; recipradius = 1.0 / radius; myloc = new vector(q); } public sphere(sphere newsphere) { isalight = newsphere.isalight; reflectivity = newsphere.getRefl(); diffusivity = newsphere.getDiff(); specularity = newsphere.getSpec(); refractivity = newsphere.getRefrac(); myred = newsphere.myred; mygreen = newsphere.mygreen; myblue = newsphere.myblue; myloc = new vector(newsphere.getlocation()); radius = newsphere.getradius(); radiussqrd = radius * radius; recipradius = 1.0 / radius; } //Modifier Methods public void setradius(double newradius) { radius = newradius; radiussqrd = radius * radius; recipradius = 1.0 / radius; } //Acsessor Methods public double getradius() { return radius; } public vector getnormal(vector outpos) { vector q = new vector((outpos.subtract(myloc)).scale(recipradius)); return q; } public vector getlocation() { vector q = new vector(myloc); return q; } public double intersect(ray r, double dist) { vector q = new vector(r.getOrig().subtract(myloc)); double b = -q.dotproduct(r.getDir()); double det = (b * b) - q.dotproduct(q) + radiussqrd; double returnmeifhit = 0.0; if(det > 0) { det = Math.sqrt(det); double i1 = b - det; double i2 = b + det; if(i2 > 0) { if(i1 < 0) { if(i2 < dist) returnmeifhit = -i2; } else if(i1 < dist) returnmeifhit = i1; } } return returnmeifhit; } } class plane extends primitive { //Variables private double d; private vector normal; //Constructor Methods public plane() { d = 1.0; normal = new vector(0.0,0.0,0.0); isalight = false; reflectivity = 0.5; diffusivity = 1.0; specularity = 1.0; refractivity = 1.0; myred = 0.5; mygreen = 0.5; myblue = 0.5; } public plane(double x, double y, double z, boolean newlight, double newrefl, double newdiff, double newspec, double newrefrac, double newred, double newgreen, double newblue, double new_D) { isalight = newlight; reflectivity = newrefl; diffusivity = newdiff; specularity = newspec; refractivity = newrefrac; myred = newred; mygreen = newgreen; myblue = newblue; d = new_D; normal = new vector(x, y, z); } public plane(vector newnorm, boolean newlight, double newrefl, double newdiff, double newspec, double newrefrac, double newred, double newgreen, double newblue, double new_D) { isalight = newlight; reflectivity = newrefl; diffusivity = newdiff; specularity = newspec; refractivity = newrefrac; myred = newred; mygreen = newgreen; myblue = newblue; d = new_D; normal = new vector(newnorm); } public plane(plane newplane) { isalight = newplane.getlight(); reflectivity = newplane.getRefl(); diffusivity = newplane.getDiff(); specularity = newplane.getSpec(); refractivity = newplane.getRefrac(); myred = newplane.myred; mygreen = newplane.mygreen; myblue = newplane.myblue; normal = new vector(newplane.getnormal()); d = newplane.getd(); } //Modifier Methods public void setnormal(vector newnorm) { normal = new vector(newnorm); } //Acsessor Methods public double getd() { return d; } public vector getnormal() { vector q = new vector(normal); return q; } public double intersect(ray r, double raydistancetraveling) { double b = normal.dotproduct(r.getDir()); if(b != 0) { double distance = -(normal.dotproduct(r.getOrig()) + d) / b; if(distance > 0) { if(distance < raydistancetraveling) { return distance; } } } return 0.0; } public vector getlocation() { return normal; } public vector getnormal(vector b) { return normal; } }