Chris Pollett >
Old Classes
> |
HW4 Solutions Page/****************************************************** * Project: CS116B Homework #4 * File: RayTracer.cpp * Purpose: draws a scene with a chessboard and with * cubes, spheres, and tetrehedrons at * positions specified by the user. * * Start date: May 11, 2005 * Programmer: Chris Pollett * * Remarks: * *******************************************************/ #ifdef WIN32 #include<windows.h> //for windows #include <GL/glut.h> #include <GL/glu.h> #endif #ifdef __APPLE__ //for MAC /* I created my project under xcode. I chose new C++ tool as the kind of project. Then under External frameworks and libraries I added the two frameworks: OpenGL.framework and GLUT.framework. (Frameworks are in /Library/Frameworks) */ #include <OpenGL/gl.h> #include <OpenGL/glu.h> #include <GLUT/glut.h> #endif #ifdef linux // for linux /*My compile line was: g++ -I /usr/X11R6/include -L /usr/X11R6/lib -lglut -lGL \ -lGLU -lX11 -lXmu -lXi -lm name.cpp -o name */ #include <GL/glut.h> #include <GL/glu.h> #endif #include <cmath> #include <iostream> #include <vector> #include <string> using namespace std; /* CONSTANTS */ const GLdouble WHITE[3] = {1.0, 1.0, 1.0}; //RGB for white const GLdouble BLACK[3] = {0.0, 0.0, 0.0}; //RGB for black const GLdouble RED[3] = {1.0, 0.0, 0.0}; //RGB for RED const GLdouble ATTENUATION_FACTOR = 100000; // used in our lighting equations to model how light attenuates with distance const GLdouble CAMERA_POSITION[3] = {0, 100, 200}; //initial position of camera const GLdouble LOOK_AT_VECTOR[3] = {0, 0, -160}; // where camera is looking at const GLdouble UP_VECTOR[3] = {0, 1, 0}; // what direction is up for the camera const GLdouble BOARD_POSITION[3] = {0, 0, -160}; // where in the scene the board is positioned const GLdouble BOARD_EDGE_SIZE = 320.0; // how wide the board is const GLdouble BOARD_HALF_SIZE = BOARD_EDGE_SIZE/2; // useful to half this size ready for some calculations const unsigned int NUM_SQUARES = 8; //number of squares wide our chess board is const GLdouble SQUARE_EDGE_SIZE = BOARD_EDGE_SIZE/NUM_SQUARES; // pixels/per square const unsigned int MAX_DEPTH = 5; // maximum depth our ray-tracing tree should go to const GLdouble SMALL_NUMBER = .0001; // used rather than check with zero to avoid round-off problems const GLdouble SUPER_SAMPLE_NUMBER = 16; // how many random rays per pixel /* PROTOTYPES */ class RayObject; /* CLASS DEFINITIONS */ /*-----------------------------------------------*/ class Point /* PURPOSE: Used to encapsulate the properties and operations for 3 dimension points/vectors used to describe our scene REMARK: Many of the operations for Points will be used very often during ray-tracing. Since they are short and we want them to be inlined we define them in the class defintion itself using a standard one-line format (not exactly like the coding guidelines) */ { private: GLdouble _x; GLdouble _y; GLdouble _z; public: Point() {_x = 0; _y = 0; _z =0;} Point(GLdouble a, GLdouble b, GLdouble c) {_x = a; _y = b; _z =c;} Point(const GLdouble pt[]) {_x = pt[0]; _y = pt[1]; _z = pt[2];} Point(const Point& p) {_x = p._x; _y = p._y; _z = p._z;} GLdouble x(){return _x;} GLdouble y(){return _y;} GLdouble z(){return _z;} void set(GLdouble a, GLdouble b, GLdouble c) {_x = a; _y = b; _z =c;} bool isZero(){ return (_x == 0 && _y ==0 && _z==0);} GLdouble length(){return sqrt(_x*_x + _y*_y + _z*_z); } void normalize(){ GLdouble l = length(); _x /=l; _y/= l; _z /= l;} friend Point operator*(GLdouble scalar, const Point &other); //scalar products friend Point operator*(const Point &other, GLdouble scalar); Point& operator*=(GLdouble scalar) {_x *= scalar; _y *= scalar; _z *= scalar; return *this;} Point& operator/=(GLdouble scalar) {_x /= scalar; _y /= scalar; _z /= scalar; return *this;} Point operator*(const Point& other) //cross product {return Point(_y*other._z - other._y*_z, _z*other._x - _x*other._z, _x*other._y - _y*other._x); } GLdouble operator&(const Point& other)//dot Product {return _x * other._x + _y * other._y + _z * other._z; } Point operator%(const Point& other)//Hadamard Product {return Point(_x * other._x, _y * other._y, _z * other._z); } Point operator+(const Point &other) const {return Point(_x + other._x, _y + other._y, _z + other._z); } Point operator-(const Point &other) const {return Point(_x - other._x, _y - other._y, _z - other._z); } Point& operator+=(const Point &other) {_x += other._x; _y += other._y; _z += other._z; return *this;} Point& operator-=(const Point &other) {_x -= other._x; _y -= other._y; _z -= other._z; return *this;} Point& operator=(const Point &other) {_x = other._x; _y = other._y; _z = other._z; return *this;} }; /*-----------------------------------------------*/ class Light /* PURPOSE: Use to encapsulate information about a light (basically its color and position) in our scene to be ray-traced REMARK: */ { private: Point _color; Point _position; public: Light(const Point& c, const Point& p){ _color = c; _position = p;} Point color(){return _color;} Point position(){return _position;} }; /*-----------------------------------------------*/ class Line /* PURPOSE: Used to encapsulate information about lines in our scene REMARK: A ray that will be ray traced will be such a line We will adopt the convention that the _startPoint of such a line is where it is coming from and the the _endPoint is mainly used to specify the direction of the line */ { private: Point _startPt; Point _endPt; public: Line(){_startPt.set(0.0, 0.0, 0.0); _endPt.set(0.0, 0.0, 0.0);} Line(const Point& p1, const Point& p2) {_startPt = p1; _endPt = p2;} void set(const Point& p1, const Point& p2){_startPt = p1; _endPt = p2;} Point startPoint() const {return _startPt;} Point endPoint() const {return _endPt;} Point direction() const { Point p = _endPt - _startPt; p.normalize(); return p; } GLdouble length() const { Point p = _endPt - _startPt; return p.length(); } }; /*-----------------------------------------------*/ class Material /* PURPOSE: Used to hold information about how a Shape will react to various kinds of light in our lighting model REMARK: We use the Phong lighting model as the basis for calculating lighting when we do ray-tracing */ { private: Point _ambient; Point _diffuse; Point _specular; Point _transparency; GLdouble _refraction; public: Material() { _ambient.set(0.0, 0.0, 0.0); _diffuse =_ambient; _specular = _ambient; _transparency = _ambient; _refraction = 1;} Material(const Point& a, const Point& d, const Point& s, const Point& t, GLdouble r) {_ambient = a; _diffuse = d; _specular = s; _transparency = t; _refraction = r;} Material(const Material& m) {_ambient = m._ambient; _diffuse = m._diffuse; _specular = m._specular; _transparency = m._transparency; _refraction = m._refraction;} Point ambient(){ return _ambient;} Point diffuse(){ return _diffuse;} Point specular(){ return _specular;} Point transparency(){ return _transparency;} GLdouble refraction(){ return _refraction;} }; /*-----------------------------------------------*/ class Intersection /* PURPOSE: used to store information about how a ray intersects with a RayObject. REMARK: A flag is used to say if there was any intersection at all. If there is an intersection objects of this class store what was the material of the object that was intersected as well as its normal, the transmitted ray and the reflected ray. */ { private: bool _intersects; Point _point; Point _normal; Material _material; Line _reflectedRay; Line _transmittedRay; public: Intersection(){} Intersection(bool intersects, const Point& p, const Point& n, const Material& m, const Line& r, const Line& t) {_intersects = intersects; _point = p; _normal = n; _material = m; _reflectedRay = r; _transmittedRay = t;} bool intersects(){return _intersects;} Point point(){return _point;} Point normal(){return _normal;} Material material(){return _material;} Line reflectedRay(){return _reflectedRay;} Line transmittedRay(){return _transmittedRay;} void setIntersect(bool i){_intersects = i;} void setMaterial(const Material& m){_material = m;} void setValues(bool intersects, const Point& p, const Point& n, const Material& m, const Line& r, const Line& t) {_intersects = intersects; _point = p; _normal = n; _material = m; _reflectedRay = r; _transmittedRay = t;} void setValues(const Intersection& in) {_intersects = in._intersects; _point = in._point; _normal = in._normal; _material = in._material; _reflectedRay = in._reflectedRay; _transmittedRay = in._transmittedRay;} }; /*-----------------------------------------------*/ class RayObject /* PURPOSE: abstract class which serves as a base for all objects to be drawn in our ray-traced scene REMARK: */ { protected: Point _position; Material _material; public: RayObject(const Point& p, const Material& m ) {_position = p; _material = m;} virtual void intersection(const Line& l, const Point& positionOffset, Intersection& inter) = 0; // by overriding intersection in different ways control how rays hit objects in our scene }; /*-----------------------------------------------*/ class Triangle : public RayObject /* PURPOSE: Triangle object are used as one of the basic building blocks for Shape's in our scen REMARK: Triangle's and Shape's are used according to a Composite design pattern to define objects in our scene */ { private: Point _vertex0; Point _vertex1; Point _vertex2; Point _u; Point _v; Point _n; GLdouble _uv; GLdouble _uu; GLdouble _vv; GLdouble _denominator; bool _degenerate; public: Triangle(const Point& p, const Material& m, const Point& p1, const Point& p2, const Point& p3) : RayObject(p,m) { _vertex0 = p1; _vertex1 = p2; _vertex2 = p3; //compute intersection with plane of triangle _u = _vertex1 - _vertex0; _v = _vertex2 - _vertex0; _n = _u*_v; //handle last degenerates case by saying we don't intersect if(_n.length() < SMALL_NUMBER) _degenerate = true; else _degenerate = false; _n.normalize(); _uv = _u & _v; _uu = _u & _u; _vv = _v & _v; _denominator = _uv*_uv - _uu*_vv; if( abs(_denominator) < SMALL_NUMBER) _degenerate = true; } void intersection(const Line& l, const Point& positionOffset, Intersection& inter); }; /*-----------------------------------------------*/ class Shape : public RayObject /* PURPOSE: Shape's are either composite objects which represent a part of the scene to be ray-traced or our primary objects in which case they are used to model sphere's in our scene REMARK: Triangle's and Shape's are used according to a Composite design pattern to define objects in our scene */ { protected: GLdouble _radius; bool _amSphere; bool _canIntersectOnlyOneSubObject; vector<RayObject *> _subObjects; public: Shape() : RayObject(Point(0,0,0), Material()) {_radius = 0; _amSphere = false;} Shape(Point p, Material m, GLdouble radius, bool a, bool c = false) : RayObject(p,m) {_radius = radius; _amSphere = a; _canIntersectOnlyOneSubObject = c; _subObjects.clear();} ~Shape(); void setRadius(GLdouble r){_radius = r;} void addRayObject(RayObject *objects) {_subObjects.push_back(objects);} void intersection(const Line& l, const Point& positionOffset, Intersection& inter); }; /*-----------------------------------------------*/ class Quad : public Shape /* PURPOSE: encapsulate information about quadrilaterals used in our scene. Quadralaterals are used for both quick tests for ray intersection as well as subobjects of more complicated objects in our scene REMARK: */ { public: Quad(Point p, Material m, Point p1, Point p2, Point p3, Point p4); }; /*-----------------------------------------------*/ class Tetrahedron : public Shape /* PURPOSE: encapsoluates information about tetrahedrons to be drawn in our scene (in this case just one) REMARK: */ { public: Tetrahedron(Point p, GLdouble edgeSize); }; /*-----------------------------------------------*/ class Sphere : public Shape /* PURPOSE: encapsoluates information about spheres to be drawn in our scene (in this case just one) REMARK: */ { public: Sphere(Point p, GLdouble radius); }; /*-----------------------------------------------*/ class Cube : public Shape /* PURPOSE: encapsoluates information about cubes to be drawn in our scene (in this case just one) REMARK: */ { public: Cube(Point p, GLdouble edgeSize); }; /*-----------------------------------------------*/ class CheckerBoard : public Shape /* PURPOSE: encapsoluates information about checkerboards to be drawn in our scene (in this case just one) REMARK: */ { private: Quad _boundingSquare; /* this Quad is used for a quick test to see if a ray intersects our chessboard */ public: CheckerBoard(Point p); void intersection(const Line& l, const Point& positionOffset, Intersection& inter); }; /* GLOBALS */ GLsizei winWidth = 500, winHeight = 500; // used for size of window GLsizei initX = 50, initY = 50; // used for initial position of window Point lightPosition(0.0, 0.0, 0.0); /* although the ray tracer actually supports giving it a vector of lights, this program only makes use of one light which is placed at lightPosition The value is later changed from this default value to a value on the chess board.*/ Point lightColor(WHITE); // color of the light Point whiteColor(WHITE); // some abbreviations for various colors Point blackColor(BLACK); Point redColor(RED); Material whiteSquare(.1*whiteColor, .5*whiteColor, whiteColor, blackColor, 1); // some materials used by objects in the scene Material blackSquare(blackColor, .1*whiteColor, blackColor, blackColor, 1); Material sphereMaterial(blackColor, .1*whiteColor, whiteColor, blackColor, 1); Material tetrahedronMaterial(blackColor, blackColor, .1*whiteColor, whiteColor, 2.0/3.0); Material cubeMaterial(.1*redColor, .4*redColor, redColor, blackColor, 1); Shape scene(BOARD_POSITION, Material(), sqrt((double)3)*BOARD_HALF_SIZE, false); // global shape for whole scene /* IMPLEMENTATIONS */ //Triangle Class Implementations /*-----------------------------------------------*/ void Triangle::intersection(const Line& ray, const Point& positionOffset, Intersection& inter) /* PURPOSE: used to fill in an Intersection object with information about how the supplied ray intersects with the current Triangle based on the given positionOffset Point vector. RECEIVES: ray -- ray to intersect with this Triangle positionOffset -- where in the overall scene this Triangle lives inter -- Intersection object to fill in with information about the if and where the ray intersects RETURNS: REMARKS: The idea for the following way to test for triangle intersections was gotten from http://softsurfer.com/Archive/algorithm_0105/algorithm_0105.htm Later noticed was in books already had like 3D Computer Graphics by Sam Buss. The coding of it is my own. Degenerate cases handle by saying there is no intersection */ { if(_degenerate) { inter.setIntersect(false); return; } //get coordinates of triangle given our position Point position = _position + positionOffset; Point v0 = position + _vertex0; Point v1 = position + _vertex1; Point v2 = position + _vertex2; Point p0 = ray.startPoint(); Point p1 = ray.endPoint(); Point diffP = p1 - p0; GLdouble ndiffP = _n&diffP; //handle another degenerate case by saying we don't intersect if( abs(ndiffP) < SMALL_NUMBER) { inter.setIntersect(false); return; } GLdouble m = (_n & (v0 - p0))/ (_n & diffP); if( m < SMALL_NUMBER) //if m is negative thenwe don't intersect { inter.setIntersect(false); return; } Point p = p0 + m*diffP; //intersection point with plane Point w = p - v0; //Now check if in triangle GLdouble wu = w & _u; GLdouble wv = w & _v; GLdouble s = (_uv*wv - _vv*wu)/_denominator; GLdouble t = (_uv*wu - _uu*wv)/_denominator; if( s >= 0 && t >=0 && s + t <= 1) // intersect { diffP.normalize(); // now u is as in the book Point u=diffP; Point r = u - (2*(u & _n))*_n; Line reflected(p, p + r); //Transmitted vector calculated using thin lens equations from book GLdouble refractionRatio = _material.refraction(); Point t(0.0, 0.0, 0.0); GLdouble cosThetai = u & _n; GLdouble modulus = 1 - refractionRatio*refractionRatio*( 1- cosThetai*cosThetai); if( modulus > 0) { GLdouble cosThetar = sqrt(modulus); t = refractionRatio*u - ( cosThetar + refractionRatio*cosThetai)*_n; } Line transmitted(p, p + t); inter.setValues(true, p, _n, _material, reflected, transmitted); } else // don't intersect { inter.setIntersect(false); } } //Shape Class Implementations /*-----------------------------------------------*/ Shape::~Shape() /* PURPOSE: destructs this shape and gets rid of any sub-object on it RECEIVES: nothing RETURNS: nothing REMARKS: */ { for(size_t i = 0; i < _subObjects.size() ; i++) { delete _subObjects[i]; } } /*-----------------------------------------------*/ void Shape::intersection(const Line& ray, const Point& positionOffset, Intersection& inter) /* PURPOSE: used to fill in an Intersection object with information about how the supplied ray intersects with the current Shape based on the given positionOffset Point vector. RECEIVES: ray -- ray to intersect with this Shape positionOffset -- where in the overall scene this Shape lives inter -- Intersection object to fill in with information about the if and where the ray intersects RETURNS: REMARKS: note this Shape could be a composite object so we recurse through its _subObjects vector */ { Point u = ray.direction(); Point p0 = ray.startPoint(); Point position = _position + positionOffset; Point deltaP = position - p0; /* check for sphere intersection if radius is > 0. if radius ==0 assume we are dealing with a object which has subObjects which do the testing */ if(_radius > 0 || _amSphere) { GLdouble uDeltaP = u & deltaP; GLdouble discriminant = uDeltaP*uDeltaP - (deltaP & deltaP) + _radius*_radius; GLdouble s = uDeltaP - sqrt(discriminant); //other solution is on far side of sphere if( discriminant < 0 || abs(s) < SMALL_NUMBER ) { inter.setIntersect(false); return; } //calculate point of intersection Point p = p0 + s*u; Point directionP0 = p - position ; if(_amSphere) { if(s < SMALL_NUMBER) // if not in front of ray then don't intersect { inter.setIntersect(false); return; } //reflected vector calculated using equations from book Point n(directionP0); n.normalize(); Point r = u - (2*(u & n))*n; Line reflected(p, p + r); //Transmitted vector calculated using thin lens equations from book Point t(0.0, 0.0, 0.0); GLdouble refractionRatio = _material.refraction(); GLdouble cosThetai = u & n; GLdouble modulus = 1 - refractionRatio*refractionRatio*( 1- cosThetai*cosThetai); if( modulus > 0) { GLdouble cosThetar = sqrt(modulus); t = refractionRatio*u - ( cosThetar + refractionRatio*cosThetai)*n; } Line transmitted(p, p + t); inter.setValues(true, p, n, _material, reflected, transmitted); } } if(!_amSphere) { inter.setIntersect(false); Intersection interTmp; GLdouble minDistance = -1.0; GLdouble distanceTmp; size_t size = _subObjects.size(); for(size_t i = 0; i < size; i++) { _subObjects[i]->intersection(ray, position, interTmp); if(interTmp.intersects()) { Point directionCur = interTmp.point() - p0; distanceTmp = directionCur.length(); if(distanceTmp < minDistance|| minDistance < 0.0) { minDistance = distanceTmp; inter.setValues(interTmp); if(_canIntersectOnlyOneSubObject) return; } } } } } //Quad Class Implementations /*-----------------------------------------------*/ Quad::Quad(Point p, Material m, Point p1, Point p2, Point p3, Point p4) : Shape(p, m, 0, false, true) /* PURPOSE: constructs a Quad object (for quadralateral) at the given offset position made of the given material and with the supplied points RECEIVES: p -- position offset into our scene m -Material Quad is made out of p1, p2, p3, p4 - four local coordinate points (relative to p) that make up the Quad. RETURNS: REMARKS: can only intersect one of two sub-triangles unless ray is same plane as Quad */ { Point zero(0.0, 0.0, 0.0); addRayObject(new Triangle(zero, m, p1, p2, p3)); addRayObject(new Triangle(zero, m, p1, p3, p4)); } //Sphere Class Implementations /*-----------------------------------------------*/ Sphere::Sphere(Point p, GLdouble r) : Shape(p, sphereMaterial, r, true) /* PURPOSE: constructs a Sphere object at the given offset position and radius in our Scene RECEIVES: p -- position offset into our scene r -- radius of Sphere RETURNS: a Sphere object REMARKS: sphereMaterial is a global in this file. The constructor mainly just calls the base constructor with the appropriate material and with the flag for Shape telling the Shape that it is a non-composite sphere (the last paramter true to the Shape constructor) */ { } //Tetrahedron Class Implementations /*-----------------------------------------------*/ Tetrahedron::Tetrahedron(Point p, GLdouble edgeSize) : Shape(p, tetrahedronMaterial, sqrt((double)3)*edgeSize/2, false) /* PURPOSE: constructs a Tetrahedron at the given offset position and edgeSize in our Scene RECEIVES: p -- position offset into our scene edgeSize -- size of an edge of our cube RETURNS: a Tetrahedron object REMARKS: note tetrahedronMaterial is a global in this file. Since the HW description didn't say the tetrahedron was regular, we took the tetrahedron to be the one obtained by slicing the cube from a top corner through the diagonal of the bottom face */ { Point zero(0.0, 0.0, 0.0); GLdouble halfEdge = edgeSize/2; //bottom addRayObject(new Triangle(zero, tetrahedronMaterial, Point(-halfEdge, -halfEdge, -halfEdge), Point(halfEdge, -halfEdge, -halfEdge), Point(-halfEdge, -halfEdge, halfEdge))); //back addRayObject(new Triangle(zero, tetrahedronMaterial, Point(-halfEdge, -halfEdge, -halfEdge), Point(-halfEdge, -halfEdge, halfEdge), Point(-halfEdge, halfEdge, -halfEdge))); //left addRayObject(new Triangle(zero, tetrahedronMaterial, Point(-halfEdge, -halfEdge, -halfEdge), Point(-halfEdge, halfEdge, -halfEdge), Point(-halfEdge, -halfEdge, halfEdge))); //front addRayObject(new Triangle(zero, tetrahedronMaterial, Point(-halfEdge, -halfEdge, halfEdge), Point(halfEdge, -halfEdge, -halfEdge), Point(-halfEdge, halfEdge, -halfEdge))); } //Cube Class Implementations /*-----------------------------------------------*/ Cube::Cube(Point p, GLdouble edgeSize) : Shape(p, cubeMaterial, sqrt((double)3)*edgeSize/2, false) /* PURPOSE: constructs a Cube at the given offset position and edgeSize in our Scene RECEIVES: p -- position offset into our scene edgeSize -- size of an edge of our cube RETURNS: a cube object REMARKS: note cubeMaterial is a global in this file */ { GLdouble halfEdge = edgeSize/2; Point zero(0.0, 0.0, 0.0); //top addRayObject(new Quad(zero, cubeMaterial, Point(-halfEdge, halfEdge, -halfEdge), Point(halfEdge, halfEdge, -halfEdge), Point(halfEdge, halfEdge, halfEdge), Point(-halfEdge, halfEdge, halfEdge))); //bottom addRayObject(new Quad(zero, cubeMaterial, Point(-halfEdge, -halfEdge, -halfEdge), Point(halfEdge, -halfEdge, -halfEdge), Point(halfEdge, -halfEdge, halfEdge), Point(-halfEdge, -halfEdge, halfEdge))); //left addRayObject(new Quad(zero, cubeMaterial, Point(-halfEdge, -halfEdge, -halfEdge), Point(-halfEdge, halfEdge, -halfEdge), Point(-halfEdge, halfEdge, halfEdge), Point(-halfEdge, -halfEdge, halfEdge))); //right addRayObject(new Quad(zero, cubeMaterial, Point(halfEdge, -halfEdge, -halfEdge), Point(halfEdge, halfEdge, -halfEdge), Point(halfEdge, halfEdge, halfEdge), Point(halfEdge, -halfEdge, halfEdge))); //back addRayObject(new Quad(zero, cubeMaterial, Point(-halfEdge, -halfEdge, -halfEdge), Point(halfEdge, -halfEdge, -halfEdge), Point(halfEdge, halfEdge, -halfEdge), Point(-halfEdge, halfEdge, -halfEdge))); //front addRayObject(new Quad(zero, cubeMaterial, Point(-halfEdge, -halfEdge, halfEdge), Point(halfEdge, -halfEdge, halfEdge), Point(halfEdge, halfEdge, halfEdge), Point(-halfEdge, halfEdge, halfEdge))); } //CheckerBoard Class Implementations /*-----------------------------------------------*/ CheckerBoard::CheckerBoard(Point p) : Shape(), _boundingSquare(p, Material(), Point(- BOARD_HALF_SIZE, 0, - BOARD_HALF_SIZE), Point( BOARD_HALF_SIZE, 0, - BOARD_HALF_SIZE), Point( BOARD_HALF_SIZE, 0, BOARD_HALF_SIZE), Point(- BOARD_HALF_SIZE, 0, BOARD_HALF_SIZE)) /*initialize bounding square to be used for quick tests if ray intersects chess board */ /* PURPOSE: constructs a Checkerboard object at the supplied offset position RECEIVES: p - offset position to put chess board at RETURNS: REMARKS: This constructor makes use of some constant in this file concerning the chessboard. */ { } /*-----------------------------------------------*/ void CheckerBoard::intersection(const Line& ray, const Point& positionOffset, Intersection& inter) /* PURPOSE: used to calculate how a ray intersects with a Chessboard object RECEIVES: ray to bounce off of chessboard positionoOffset - offset vector for location of chessboard inter -- an Intersection object to be filled in with details on if and where ray intersects with Chessboard RETURNS: nothing REMARKS: */ { _boundingSquare.intersection(ray, positionOffset, inter); if(inter.intersects()) { Point p = inter.point() - positionOffset + Point(BOARD_HALF_SIZE, 0, BOARD_HALF_SIZE); int squareSum = int(p.x()/SQUARE_EDGE_SIZE) + int(p.z()/SQUARE_EDGE_SIZE); if((squareSum & 1) == 0) { inter.setMaterial(whiteSquare); } else { inter.setMaterial(blackSquare); } } } //CLASSLESS FUNCTIONS /*-----------------------------------------------*/ Point operator*(GLdouble scalar, const Point& p) /* PURPOSE: multiplies the supplied point vector by the scalar amount RECEIVES: p - Point vector (x,y,z) scalar -- the scalar `a' to multiply by RETURNS: the point vector (a*x, a*y, a*z) REMARKS: */ { return Point(scalar*p._x, scalar*p._y, scalar*p._z); } /*-----------------------------------------------*/ Point operator*(const Point& p, GLdouble scalar) /* PURPOSE: multiplies the supplied point vector by the scalar amount RECEIVES: p - Point vector (x,y,z) scalar -- the scalar `a' to multiply by RETURNS: the point vector (a*x, a*y, a*z) REMARKS: */ { return Point(scalar*p._x, scalar*p._y, scalar*p._z); } /*-----------------------------------------------*/ Point randomUnit() /* PURPOSE: generate a random vector of length 1 RECEIVES: Nothing RETURNS: Nothing REMARKS: */ { //generate random point within unit sphere Point vec(0.0, 0.0, 0.0); while(vec.isZero()) { vec = Point(double(rand())/(RAND_MAX+1.0) - .5, double(rand())/(RAND_MAX+1.0) - .5, double(rand())/(RAND_MAX+1.0) - .5); } vec.normalize(); //push out to unit sphere return vec; } /*-----------------------------------------------*/ inline GLdouble attenuation(GLdouble distance) /* PURPOSE: calculates how much light intensities will decay with distance RECEIVES: distance -- to use RETURNS: decimal value between 0 and 1 by which an intensity at the given distance would be reduced REMARKS: */ { return ATTENUATION_FACTOR/(ATTENUATION_FACTOR + distance*distance); } /*-----------------------------------------------*/ void rayTraceRay(Shape& scene, vector<Light> lights, const Line& ray, Point& color, unsigned int depth) /* PURPOSE: Does ray tracing of a single ray in a scene according to the supplied lights to the perscribed depth RECEIVES: scene -- Shape to do ray-tracing one lights -- Light's which are lighting the scene ray -- to be used for ray-tracing consists of two points (starting point to do ray-tracing from plus another point which together give the direction of the initial ray.) color -- used to store the color returned by doing the ray tracing depth -- in terms of tree of sub-rays we calculate RETURNS: Nothing REMARKS: */ { Intersection intersection; scene.intersection(ray, Point(0.0, 0.0, 0.0), intersection); if(!intersection.intersects()) return; Point pt = intersection.point(); Material material = intersection.material(); Line reflectedRay = intersection.reflectedRay(); Line transmittedRay = intersection.transmittedRay(); Line shadowRay; Point lColor; size_t size = lights.size(); for(size_t i = 0 ; i < size; i++) { shadowRay.set(pt, lights[i].position()); Intersection shadowIntersection; scene.intersection(shadowRay, Point(0.0, 0.0, 0.0), shadowIntersection ); if(!shadowIntersection.intersects() || !shadowIntersection.material().transparency().isZero()) { lColor = attenuation(shadowRay.length())*lights[i].color(); color += (material.ambient()% lColor) + abs(intersection.normal() & shadowRay.direction())*(material.diffuse()% lColor) + abs(ray.direction() & reflectedRay.direction())*(material.specular()% lColor); } } if(depth > 0) { Point transmittedColor(0.0, 0.0, 0.0); Point reflectedColor(0.0, 0.0, 0.0); Point transparency = material.transparency(); Point opacity = Point(1.0, 1.0, 1.0) - transparency; if(!transparency.isZero() && transparency.length() > SMALL_NUMBER) //if not transparent then don't send ray { rayTraceRay(scene, lights, transmittedRay, transmittedColor, depth - 1); color += (transparency % transmittedColor); } if(!opacity.isZero()) // if completely transparent don't send reflect ray { rayTraceRay(scene, lights, reflectedRay, reflectedColor, depth - 1); color += (opacity % reflectedColor); } } } /*-----------------------------------------------*/ void rayTraceScreen(Shape& scene, vector<Light>& lights, Point camera, Point lookAt, Point up, int bottomX, int bottomY, int width, int height) /* PURPOSE: Does the ray-tracing of a shape according to the supplied lights, camera dimension and screen dimensions RECEIVES: scene -- Shape to be ray-traced (Shapes use the Composite pattern so are made of sub-shapes light -- a vector of Light's used to light the scene camera -- location of the viewing position lookat -- where one is looking at from this position up -- what direction is up from this position bottomX -- how far to the left from the lookat point is the start of the screen bottomy -- how far down from the lookat point is the start of the screen width -- width of the screen height -- height of the screen RETURNS: Nothing REMARKS: */ { Point lookDirection = lookAt - camera; Point right = lookDirection * up; right.normalize(); Point rightOffset = width*right; up = right*lookDirection; up.normalize(); Point screenPt = lookAt + bottomX*right + bottomY*up; Line ray; Point color(0.0, 0.0, 0.0); Point avgColor(0.0, 0.0, 0.0); Point weightedColor(0.0, 0.0, 0.0); Point oldWeightedColor(0.0, 0.0, 0.0); GLdouble k; glBegin(GL_POINTS); for(int j = 0; j < height; j++) { for(int i = 0; i < width; i++) { for(k = 0.0; k < SUPER_SAMPLE_NUMBER; k++) { ray.set(camera, screenPt + .5*randomUnit() ); color.set(0.0, 0.0, 0.0); rayTraceRay(scene, lights, ray, color, MAX_DEPTH); oldWeightedColor = (k + 1.0)*avgColor; avgColor += color; weightedColor = k*avgColor; if((weightedColor - oldWeightedColor).length() < SMALL_NUMBER*k*(k+1)) break; } //if ( k < SUPER_SAMPLE_NUMBER && k >1) cout <<"hello"<<k<<endl; avgColor /= k; glColor3d(avgColor.x(), avgColor.y(), avgColor.z()); glVertex2i(i, j); screenPt += right; } screenPt -= rightOffset; screenPt += up; } glEnd(); } /*-----------------------------------------------*/ Point convertStringCoordinate(string coordString) /* PURPOSE: Converts a string of two characters of the form: letter row + number color (for example, b4) into coordinates for a piece of our ray tracing work. RECEIVES: coordString -- string to convert RETURNS: Nothing REMARKS: */ { Point firstSquare(-BOARD_EDGE_SIZE/2, 0.0, BOARD_EDGE_SIZE/2); Point rowOffset(0.0, 0.0, -(double(coordString[0] - 'a') + .5)*SQUARE_EDGE_SIZE); //negative because farther back == higher row number Point colOffset((double(coordString[1] - '0' - 1) + .5)*SQUARE_EDGE_SIZE, 0.0, 0.0); Point heightOffset(0.0, 1.5*SQUARE_EDGE_SIZE, 0.0); Point square = firstSquare + rowOffset + colOffset + heightOffset; return square; } /*-----------------------------------------------*/ void init() /* PURPOSE: gets locations of objects from users sets the background color to black, sets up display list for coordinate axes, initializes extrusions vector RECEIVES: Nothing RETURNS: Nothing REMARKS: */ { Point boardPosition(0.0, 0.0, 0.0); CheckerBoard *checkerBoard = new CheckerBoard(boardPosition); scene.addRayObject(checkerBoard); string tmp; cout << "Please enter the position of the light:\n"; cin >> tmp; //tmp ="b6"; //commented values are nice values to demonstrate the ray tracer lightPosition = Point(BOARD_POSITION) + Point(0.0, 3.5*SQUARE_EDGE_SIZE, 0.0) + convertStringCoordinate(tmp); //with what convertStringCoordinate gives makes 5 squares above board cout << "Please enter the position of the tetrahedron:\n"; cin >>tmp; //tmp = "b4"; Tetrahedron *tetrahedron = new Tetrahedron(convertStringCoordinate(tmp), SQUARE_EDGE_SIZE); scene.addRayObject(tetrahedron); cout << "Please enter the position of the sphere:\n"; cin >> tmp; //tmp = "d7"; Sphere *sphere = new Sphere(convertStringCoordinate(tmp), SQUARE_EDGE_SIZE/2); scene.addRayObject(sphere); cout << "Please enter the position of the cube:\n"; cin >> tmp; //tmp = "a7"; Cube *cube = new Cube(convertStringCoordinate(tmp), SQUARE_EDGE_SIZE); scene.addRayObject(cube); glClearColor(0.0, 0.0, 0.0, 0.0); glViewport(0, 0, winWidth, winHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, winWidth, 0, winHeight); } /*-----------------------------------------------*/ void drawFn() /* PURPOSE: Used to craw the complete ray-traced chessboard RECEIVES: Nothing RETURNS: Nothing REMARKS: */ { glClear(GL_COLOR_BUFFER_BIT); vector<Light> lights; lights.push_back(Light(lightColor, lightPosition)); Point camera(CAMERA_POSITION); Point lookAt(LOOK_AT_VECTOR); Point up(UP_VECTOR); rayTraceScreen(scene, lights, camera, lookAt, up, -winWidth/2, -winHeight/2, winWidth, winHeight); glFlush(); } /*-----------------------------------------------*/ void reshapeFn(int newWidth, int newHeight) /* PURPOSE: To redraw scene when window gets resized. RECEIVES: newWidth -- new window x size newHeight -- new window y size RETURNS: Nothing REMARKS: */ { glViewport(0, 0, newWidth, newHeight); winWidth = newWidth; winHeight = newHeight; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, winWidth, 0, winHeight); glClear(GL_COLOR_BUFFER_BIT); } /*-----------------------------------------------*/ int main (int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowPosition(initX, initY); glutInitWindowSize(winWidth, winHeight); glutCreateWindow("Ray Trace"); init(); glutDisplayFunc(drawFn); glutReshapeFunc(reshapeFn); glutMainLoop(); return 0; } |