bool Sphere::intersectLocal(ray& r, isect& i) const { Vec3d v = -r.getPosition(); double b = v * r.getDirection(); double discriminant = b*b - v*v + 1; if( discriminant < 0.0 ) { return false; } discriminant = sqrt( discriminant ); double t2 = b + discriminant; if( t2 <= RAY_EPSILON ) { return false; } i.obj = this; i.setMaterial(this->getMaterial()); double t1 = b - discriminant; if( t1 > RAY_EPSILON ) { i.t = t1; i.N = r.at( t1 ); i.N.normalize(); } else { i.t = t2; i.N = r.at( t2 ); i.N.normalize(); } return true; }
// Intersect ray r with the triangle abc. If it hits returns true, // and puts the t parameter, barycentric coordinates, normal, object id, // and object material in the isect object bool TrimeshFace::intersectLocal( const ray& r, isect& i ) const { const Vec3d& a = parent->vertices[ids[0]]; const Vec3d& b = parent->vertices[ids[1]]; const Vec3d& c = parent->vertices[ids[2]]; // tangent vectors Vec3d t1 = b - a; Vec3d t2 = c - a; Vec3d n = crossProd(t1,t2); double D = -n*a; // if the surface is parallel to the ray there is no intersection if(r.getDirection()*n == 0) { return false; } double t = -(n*r.getPosition() + D)/(n*r.getDirection() ); if (t <= RAY_EPSILON) return false; // point of intersection with the same plane (doesn't mean intersection with triangle) p(t)=p+t*d Vec3d p = r.at(t); // triangle area double A = n.length()/2.0; // barycentric coords double wa = crossProd(c-b, p-b).length() / (2.0*A); double wb = crossProd(a-c, p-c).length() / (2.0*A); double wc = crossProd(b-a, p-a).length() / (2.0*A); if((wa >= 0.0) && (wb >= 0.0) && (wc >= 0.0) && (wa+wb+wc-1.0 <= 0.00001)) { i.setT(t); i.setBary(wa, wb, wc); if (parent->normals.size() == 0) { i.setN(n); } else { Vec3d inter_n = wa*parent->normals[ids[0]] + wb*parent->normals[ids[1]] + wc*parent->normals[ids[2]]; inter_n.normalize(); i.setN(inter_n); } i.setObject(this); if (parent->materials.size() == 0) { i.setMaterial(this->getMaterial() ); } else { Material inter_m = wa*(*parent->materials[ids[0]]); inter_m += wb*(*parent->materials[ids[1]]); inter_m += wc*(*parent->materials[ids[2]]); i.setMaterial(inter_m); } return true; } return false; }
//Test bool Square::intersectLocal(ray& r, isect& i) const { Vec3d p = r.getPosition(); Vec3d d = r.getDirection(); if( d[2] == 0.0 ) { return false; } double t = -p[2]/d[2]; if( t <= RAY_EPSILON ) { return false; } Vec3d P = r.at( t ); if( P[0] < -0.5 || P[0] > 0.5 ) { return false; } if( P[1] < -0.5 || P[1] > 0.5 ) { return false; } i.obj = this; i.setMaterial(this->getMaterial()); i.t = t; if( d[2] > 0.0 ) { i.N = Vec3d( 0.0, 0.0, -1.0 ); } else { i.N = Vec3d( 0.0, 0.0, 1.0 ); } i.setUVCoordinates( Vec2d(P[0] + 0.5, P[1] + 0.5) ); return true; }
// Get any intersection with an object. Return information about the // intersection through the reference parameter. bool Scene::intersect( const ray& r, isect& i ) const { double tmin = 0.0; double tmax = 0.0; bool have_one = false; typedef vector<Geometry*>::const_iterator iter; for( iter j = objects.begin(); j != objects.end(); ++j ) { isect cur; if( (*j)->intersect( r, cur ) ) { if( !have_one || (cur.t < i.t) ) { i = cur; have_one = true; } } } if( !have_one ) i.setT(1000.0); // if debugging, intersectCache.push_back( std::make_pair(r,i) ); return have_one; }
//Test // now the object is in the local coordinate rather than the global bool Square::intersectLocal( const ray& r, isect& i ) const { // get the parameters of the ray Vec3d p = r.getPosition(); Vec3d d = r.getDirection(); // if the ray is perpendicular to the z-axis if( d[2] == 0.0 ) { return false; } // calculate the value of t double t = -p[2]/d[2]; // if the intersection is too close to the source // then we don't count that as a intersection if( t <= RAY_EPSILON ) { return false; } Vec3d P = r.at( t ); if( P[0] < -0.5 || P[0] > 0.5 ) { return false; } if( P[1] < -0.5 || P[1] > 0.5 ) { return false; } i.obj = this; i.t = t; if( d[2] > 0.0 ) { i.N = Vec3d( 0.0, 0.0, -1.0 ); } else { i.N = Vec3d( 0.0, 0.0, 1.0 ); } i.setUVCoordinates( Vec2d(P[0] + 0.5, P[1] + 0.5) ); return true; }
bool Trimesh::intersectLocal(const ray&r, isect&i) const { double tmin = 0.0; double tmax = 0.0; typedef Faces::const_iterator iter; bool have_one = false; for( iter j = faces.begin(); j != faces.end(); ++j ) { isect cur; if( (*j)->intersectLocal( r, cur ) ) { if( !have_one || (cur.t < i.t) ) { i = cur; have_one = true; } } } if( !have_one ) i.setT(1000.0); return have_one; }
// Apply the phong model to this point on the surface of the object, returning // the color of that point. Vec3d Material::shade(Scene *scene, const ray& r, const isect& i) const { const Material& m = i.getMaterial(); Vec3d I = m.ke(i) + prod(m.ka(i) ,scene->ambient()); Vec3d R = 2*(-r.getDirection() * i.N)*i.N +r.getDirection(); for ( vector<Light*>::const_iterator litr = scene->beginLights(); litr != scene->endLights(); ++litr ) { Vec3d atten = (*litr)->distanceAttenuation(r.at(i.t)) * (*litr)->shadowAttenuation(r,r.at(i.t)); I += prod(atten,(m.kd(i)*max((i.N * (*litr)->getDirection(r.at(i.t)) ), 0.0) + m.ks(i) * max(((scene->getCamera().getEye() - r.at(i.t)) *R),0.0))); } // You will need to call both the distanceAttenuation() and // shadowAttenuation() methods for each light source in order to // compute shadows and light falloff. return I; }
vec3f RayTracer::traceRay( Scene *scene, const ray& r, const vec3f& thresh, int depth, isect& i, vector<const SceneObject*>& stack ) { if( depth>=0 && thresh[0] > threshold - RAY_EPSILON && thresh[1] > threshold - RAY_EPSILON && thresh[2] > threshold - RAY_EPSILON && scene->intersect( r, i ) ) { // YOUR CODE HERE // An intersection occured! We've got work to do. For now, // this code gets the material for the surface that was intersected, // and asks that material to provide a color for the ray. // This is a great place to insert code for recursive ray tracing. // Instead of just returning the result of shade(), add some // more steps: add in the contributions from reflected and refracted // rays. const Material& m = i.getMaterial(); vec3f color = m.shade(scene, r, i); //calculate the reflected ray vec3f d = r.getDirection(); vec3f position = r.at(i.t); vec3f direction = d - 2 * i.N * d.dot(i.N); ray newray(position, direction); if(!m.kr.iszero()) { vec3f reflect = m.kr.multiply(traceRay(scene, newray, thresh.multiply(m.kr), depth-1, stack).clamp()); color += reflect; } //calculate the refracted ray double ref_ratio; double sin_ang = d.cross(i.N).length(); vec3f N = i.N; //Decide going in or out const SceneObject *mi = NULL, *mt = NULL; int stack_idx = -1; vector<const SceneObject*>::reverse_iterator itr; //1 use the normal to decide whether to go in or out //0: travel through, 1: in, 2: out char travel = 0; if(i.N.dot(d) <= -RAY_EPSILON) { //from outer surface in //test whether the object has two face ray test_ray(r.at(i.t) + d * 2 * RAY_EPSILON, -d); isect test_i; if(i.obj->intersect(r, test_i) && test_i.N.dot(N) > -RAY_EPSILON) { //has interior travel = 1; } } else { travel = 2; } if(travel == 1) { if(!stack.empty()) { mi = stack.back(); } mt = i.obj; stack.push_back(mt); } else if(travel == 2) { //if it is in our stack, then we must pop it for(itr = stack.rbegin(); itr != stack.rend(); ++itr) { if(*itr == i.obj) { mi = *itr; vector<const SceneObject*>::iterator ii = itr.base() - 1; stack_idx = ii - stack.begin(); stack.erase(ii); break; } } if(!stack.empty()) { mt = stack.back(); } } if(N.dot(d) >= RAY_EPSILON) { N = -N; } ref_ratio = (mi?(mi->getMaterial().index):1.0) / (mt?(mt->getMaterial().index):1.0); if(!m.kt.iszero() && (ref_ratio < 1.0 + RAY_EPSILON || sin_ang < 1.0 / ref_ratio + RAY_EPSILON)) { //No total internal reflection //We do refraction now double c = N.dot(-d); direction = (ref_ratio * c - sqrt(1 - ref_ratio * ref_ratio * (1 - c * c))) * N + ref_ratio * d; newray = ray(position, direction); vec3f refraction = m.kt.multiply(traceRay(scene, newray, thresh.multiply(m.kt), depth-1, stack).clamp()); color += refraction; } if(travel == 1) { stack.pop_back(); } else if(travel == 2) { if(mi) { stack.insert(stack.begin() + stack_idx, mi); } } return color; } else { // No intersection. This ray travels to infinity, so we color // it according to the background color, which in this (simple) case // is just black. if(m_bBackground && bg) { double u, v; angleToSphere(r.getDirection(), u, v); //Scale to [0, 1]; u /= 2 * M_PI; v /= M_PI; int tx = int(u * bg_width), ty = bg_height - int(v * bg_height); return vec3f(bg[3 * (ty * bg_width + tx)] / 255.0, bg[3 * (ty * bg_width + tx) + 1] / 255.0, bg[3 * (ty * bg_width + tx) + 2] / 255.0); } else { return vec3f( 0.0, 0.0, 0.0 ); } } }
bool Box::intersectLocal( const ray& r, isect& i ) const { Vec3d p = r.getPosition(); Vec3d d = r.getDirection(); int it; double x, y, t, bestT; int mod0, mod1, mod2, bestIndex; bestT = HUGE_DOUBLE; bestIndex = -1; for(it=0; it<6; it++){ mod0 = it%3; if(d[mod0] == 0){ continue; } t = ((it/3) - 0.5 - p[mod0]) / d[mod0]; if(t < RAY_EPSILON || t > bestT){ continue; } mod1 = (it+1)%3; mod2 = (it+2)%3; x = p[mod1]+t*d[mod1]; y = p[mod2]+t*d[mod2]; if( x<=0.5 && x>=-0.5 && y<=0.5 && y>=-0.5) { if(bestT > t){ bestT = t; bestIndex = it; } } } if(bestIndex < 0) return false; i.setT(bestT); i.setObject(this); Vec3d intersect_point = r.at(i.t); int i1 = (bestIndex + 1) % 3; int i2 = (bestIndex + 2) % 3; if(bestIndex < 3) { i.setN(Vec3d(-double(bestIndex == 0), -double(bestIndex == 1), -double(bestIndex == 2))); i.setUVCoordinates( Vec2d( 0.5 - intersect_point[ min(i1, i2) ], 0.5 + intersect_point[ max(i1, i2) ] ) ); } else { i.setN(Vec3d(double(bestIndex==3), double(bestIndex == 4), double(bestIndex == 5))); i.setUVCoordinates( Vec2d( 0.5 + intersect_point[ min(i1, i2) ], 0.5 + intersect_point[ max(i1, i2) ] ) ); } return true; }
bool Cone::intersectLocal(ray& r, isect& i) const { bool ret = false; const int x = 0, y = 1, z = 2; // For the dumb array indexes for the vectors Vec3d normal; Vec3d R0 = r.getPosition(); Vec3d Rd = r.getDirection(); double pz = R0[2]; double dz = Rd[2]; double a = Rd[x]*Rd[x] + Rd[y]*Rd[y] - beta_squared * Rd[z]*Rd[z]; if( a == 0.0) return false; // We're in the x-y plane, no intersection double b = 2 * (R0[x]*Rd[x] + R0[y]*Rd[y] - beta_squared * ((R0[z] + gamma) * Rd[z])); double c = -beta_squared*(gamma + R0[z])*(gamma + R0[z]) + R0[x] * R0[x] + R0[y] * R0[y]; double discriminant = b * b - 4 * a * c; double farRoot, nearRoot, theRoot = RAY_EPSILON; bool farGood, nearGood; if(discriminant <= 0) return false; // No intersection discriminant = sqrt(discriminant); // We have two roots, so calculate them nearRoot = (-b + discriminant) / ( 2 * a ); farRoot = (-b - discriminant) / ( 2 * a ); // This is confusing, but it figures out which // root is closer and puts into theRoot nearGood = isGoodRoot(r.at(nearRoot)); if(nearGood && (nearRoot > theRoot)) { theRoot = nearRoot; normal = Vec3d((r.at(theRoot))[x], (r.at(theRoot))[y], -2.0 * beta_squared * (r.at(theRoot)[z] + gamma)); } farGood = isGoodRoot(r.at(farRoot)); if(farGood && ( (nearGood && farRoot < theRoot) || farRoot > RAY_EPSILON) ) { theRoot = farRoot; normal = Vec3d((r.at(theRoot))[x], (r.at(theRoot))[y], -2.0 * beta_squared * (r.at(theRoot)[z] + gamma)); } // In case we are _inside_ the _uncapped_ cone, we need to flip the normal. // Essentially, the cone in this case is a double-sided surface // and has _2_ normals if( !capped && (normal * r.getDirection()) > 0 ) normal = -normal; // These are to help with finding caps double t1 = (-pz)/dz; double t2 = (height-pz)/dz; Vec3d p( r.at( t1 ) ); if(capped) { if( p[0]*p[0] + p[1]*p[1] <= b_radius*b_radius) { if(t1 < theRoot && t1 > RAY_EPSILON) { theRoot = t1; if( dz > 0.0 ) { // Intersection with cap at z = 0. normal = Vec3d( 0.0, 0.0, -1.0 ); } else { normal = Vec3d( 0.0, 0.0, 1.0 ); } } } Vec3d q( r.at( t2 ) ); if( q[0]*q[0] + q[1]*q[1] <= t_radius*t_radius) { if(t2 < theRoot && t2 > RAY_EPSILON) { theRoot = t2; if( dz > 0.0 ) { // Intersection with interior of cap at z = 1. normal = Vec3d( 0.0, 0.0, 1.0 ); } else { normal = Vec3d( 0.0, 0.0, -1.0 ); } } } } if(theRoot <= RAY_EPSILON) return false; i.setT(theRoot); normal.normalize(); i.setN(normal); i.obj = this; i.setMaterial(this->getMaterial()); return true; return ret; }