//! Intersect ray and sphere, returning true if there is an intersection. bool intersect(const ray<Type>& r, Type& tmin, Type& tmax) const { const vec3<Type> r_to_s = r.getOrigin() - m_center; //Compute A, B and C coefficients const Type A = r.getDirection().sqrLength(); const Type B = 2.0f * r_to_s.dot(r.getDirection()); const Type C = r_to_s.sqrLength() - m_radius * m_radius; //Find discriminant Type disc = B * B - 4.0 * A * C; // if discriminant is negative there are no real roots if (disc < 0.0) return false; disc = (Type)std::sqrt((double)disc); tmin = (-B + disc) / (2.0 * A); tmax = (-B - disc) / (2.0 * A); // check if we're inside it if ((tmin < 0.0 && tmax > 0) || (tmin > 0 && tmax < 0)) return false; if (tmin > tmax) std::swap(tmin, tmax); return (tmin > 0); }
Segments CSGNode::intersectLocal(const ray& r) const{ Segments ret; if (isLeaf){ SegmentPoint pNear, pFar; isect i; ray backR(r.at(-10000), r.getDirection()); if(!item->intersect(backR, i))return ret; pNear.t = i.t - 10000; pNear.normal = i.N; pNear.isRight = false; ray contiR(r.at(pNear.t+RAY_EPSILON*10),r.getDirection()); if (!item->intersect(contiR, i))pFar = pNear; else { pFar.t = i.t + pNear.t; pFar.normal = i.N; } pFar.isRight = true; ret.addPoint(pNear); ret.addPoint(pFar); return ret; } else { if (!lchild || !rchild)return ret; Segments leftSeg, rightSeg; leftSeg = lchild->intersectLocal(r); rightSeg = rchild->intersectLocal(r); leftSeg.Merge(rightSeg,relation); return leftSeg; } }
bool Cone::intersectBody( const ray& r, isect& i ) const { vec3f d = r.getDirection(); vec3f p = r.getPosition(); double a = (d[0]*d[0]) + (d[1]*d[1]) - (C*d[2]*d[2]); double b = 2.0 * (d[0]*p[0] + d[1]*p[1] - C*d[2]*p[2]) - B*d[2]; double c = (p[0]*p[0]) + (p[1]*p[1]) - A - (B*p[2]) - (C*p[2]*p[2]); double disc = b*b - 4.0*a*c; if( disc <= 0.0 ) { return false; } disc = sqrt( disc ); double t1 = (-b - disc) / (2.0 * a); double t2 = (-b + disc) / (2.0 * a); if( t2 < RAY_EPSILON ) { return false; } if( t1 > RAY_EPSILON ) { // Two intersections. vec3f P = r.at( t1 ); double z = P[2]; if( z >= 0.0 && z <= height ) { // It's okay. i.t = t1; double p3 = -C*P[2] + (b_radius - t_radius)*b_radius / height; i.N = vec3f(P[0], P[1], p3).normalize(); #ifdef _DEBUG printf("two intersections!\n"); #endif return true; } } vec3f P = r.at( t2 ); double z = P[2]; if( z >= 0.0 && z <= height ) { i.t = t2; double p3 = -C*P[2] + (b_radius - t_radius)*b_radius / height; i.N = vec3f(P[0], P[1], p3).normalize(); // 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 && (i.N).dot( r.getDirection() ) > 0 ) i.N = -i.N; #ifdef _DEBUG printf("one intersection!\n"); #endif return true; } return false; }
// 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; }
// Apply the Blinn-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 { // YOUR CODE HERE // For now, this method just returns the diffuse color of the object. // This gives a single matte color for every distinct surface in the // scene, and that's it. Simple, but enough to get you started. // (It's also inconsistent with the Phong model...) // Your mission is to fill in this method with the rest of the phong // shading model, including the contributions of all the light sources. // You will need to call both distanceAttenuation() and shadowAttenuation() // somewhere in your code in order to compute shadows and light falloff. if (debugMode) std::cout << "Debugging the Phong code (or lack thereof...)" << std::endl; // When you're iterating through the lights, // you'll want to use code that looks something // like this: Vec3d light = ke(i); Vec3d normal = i.N; Vec3d iDot = r.at(i.t); if (r.getDirection() * normal > 0) { normal = -normal; light += prod(prod(scene->ambient(), ka(i)), kt(i)); } else { light += prod(scene->ambient(), ka(i)); } for (vector<Light*>::const_iterator litr = scene->beginLights(); litr != scene->endLights(); ++litr) { Light* pLight = *litr; double distAttenuation = pLight->distanceAttenuation(iDot); Vec3d shadowAttenuation = pLight->shadowAttenuation(iDot); Vec3d atten = distAttenuation * shadowAttenuation; Vec3d L = pLight->getDirection(iDot); if (L * normal > 0) { Vec3d H = (L + -1 * r.getDirection()); if (H.length() != 0) H.normalize(); double sDot = max(0.0, normal * H); Vec3d dTerm = kd(i) * (normal * L); Vec3d sTerm = ks(i) * (pow(sDot, shininess(i))); Vec3d newLight = dTerm + sTerm; newLight = prod(newLight, pLight->getColor()); light += prod(atten, newLight); } } return light; }
bool Cone::intersectBody( const ray& r, isect& i ) const { vec3f d = r.getDirection(); vec3f p = r.getPosition(); double a = (d[0]*d[0]) + (d[1]*d[1]) - (C*d[2]*d[2]); double b = 2.0 * (d[0]*p[0] + d[1]*p[1] - C*d[2]*p[2]) - B*d[2]; double c = (p[0]*p[0]) + (p[1]*p[1]) - A - (B*p[2]) - (C*p[2]*p[2]); double disc = b*b - 4.0*a*c; if( disc <= 0.0 ) { return false; } disc = sqrt( disc ); double t1 = (-b - disc) / (2.0 * a); double t2 = (-b + disc) / (2.0 * a); if( t2 < RAY_EPSILON ) { return false; } if( t1 > RAY_EPSILON ) { // Two intersections. vec3f P = r.at( t1 ); double z = P[2]; if( z >= 0.0 && z <= height ) { double n3 = -C*P[2] + (b_radius - t_radius)*b_radius / height; i.t = t1; i.N = vec3f( P[0], P[1], n3).normalize(); if (!capped && (i.N).dot(r.getDirection()) > 0) i.N = -i.N; return true; } } vec3f P = r.at( t2 ); double z = P[2]; if( z >= 0.0 && z <= height ) { double n3 = -C*P[2] + (b_radius - t_radius)*b_radius / height; i.t = t2; i.N = vec3f( P[0], P[1], n3).normalize(); if( !capped && (i.N).dot( r.getDirection() ) > 0 ) i.N = -i.N; return true; } return false; }
bool intersectCircle(const ray& r0, double& tnear, double& tfar, const vec3f& center) { ray r(r0.getPosition() - center, r0.getDirection()); vec3f v = -r.getPosition(); double b = v.dot(r.getDirection()); double discriminant = b*b - v.dot(v) + 1; if (discriminant < 0.0) { return false; } discriminant = sqrt(discriminant); double t2 = b + discriminant; if (t2 <= RAY_EPSILON) { return false; } double t1 = b - discriminant; if (t1 > RAY_EPSILON) { tnear = t1; tfar = t2; } else { tnear = 0; tfar = t2; } return true; }
bool Square::intersectLocal( const ray& r, isect& i ) const { vec3f p = r.getPosition(); vec3f d = r.getDirection(); if( d[2] == 0.0 ) { return false; } double t = -p[2]/d[2]; if( t <= RAY_EPSILON ) { return false; } vec3f 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 = vec3f( 0.0, 0.0, -1.0 ); } else { i.N = vec3f( 0.0, 0.0, 1.0 ); } return true; }
// Apply the phong model to this point on the surface of the object, returning // the color of that point. Uses shaddowAttenuation which sends a shadow ray // to check if there is an intersection that blocks the light sources. Vec3d Material::shade( Scene *scene, const ray& r, const isect& i ) const { Vec3d retVal = ke(i) + prod(ka(i), scene->ambient()); // Applies calculations for each light source for (vector<Light*>::const_iterator litr = scene->beginLights(); litr != scene->endLights(); ++litr) { Vec3d point = r.getPosition() + r.getDirection() * i.t; Light* pLight = *litr; Vec3d reflectionAngle = 2 * (i.N * pLight->getDirection(point)) * i.N - pLight->getDirection(point); Vec3d diffIntensity = kd(i) * (max(0, i.N * pLight->getDirection(point))); Vec3d viewerAngle = scene->getCamera().getEye() - point; viewerAngle.normalize(); Vec3d specIntensity = ks(i) * pow(max(0, viewerAngle * reflectionAngle), shininess(i)); Vec3d lcolor = pLight->getColor(point); Vec3d totalColor = prod(diffIntensity + specIntensity, lcolor); totalColor = totalColor * pLight->distanceAttenuation(point); totalColor = prod(totalColor, pLight->shadowAttenuation(point)); retVal = retVal + totalColor; } return retVal; }
bool CSGTree::intersect(const ray& r, isect& i) const{ if (!root)return false; Segments inters = root->intersectLocal(r); SegmentPoint sp; if(!inters.firstPositive(sp))return false; i.t = sp.t; if (sp.isRight){//right - out if (sp.normal*r.getDirection() > RAY_EPSILON)i.N = sp.normal; else i.N = -sp.normal; } else {//left - in if (sp.normal*r.getDirection() > RAY_EPSILON)i.N = -sp.normal; else i.N = sp.normal; } return true; }
bool Sphere::intersectLocal( const 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; 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; }
bool Cylinder::intersectCaps( const ray& r, isect& i ) const { if( !capped ) { return false; } double pz = r.getPosition()[2]; double dz = r.getDirection()[2]; if( 0.0 == dz ) { return false; } double t1; double t2; if( dz > 0.0 ) { t1 = (-pz)/dz; t2 = (1.0-pz)/dz; } else { t1 = (1.0-pz)/dz; t2 = (-pz)/dz; } if( t2 < RAY_EPSILON ) { return false; } if( t1 >= RAY_EPSILON ) { vec3f p( r.at( t1 ) ); if( (p[0]*p[0] + p[1]*p[1]) <= 1.0 ) { i.t = t1; if( dz > 0.0 ) { // Intersection with cap at z = 0. i.N = vec3f( 0.0, 0.0, -1.0 ); } else { i.N = vec3f( 0.0, 0.0, 1.0 ); } return true; } } vec3f p( r.at( t2 ) ); if( (p[0]*p[0] + p[1]*p[1]) <= 1.0 ) { i.t = t2; if( dz > 0.0 ) { // Intersection with cap at z = 1. i.N = vec3f( 0.0, 0.0, 1.0 ); } else { i.N = vec3f( 0.0, 0.0, -1.0 ); } return true; } return false; }
// 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; }
bool Metaball::intersectLocal(const ray& r, isect& i) const { bool inside = false; if (calvalue(r.getPosition(), ball1pos, ball2pos)>threshold)inside = true; //determine possible intersect range double t11=0, t12=0, t21=0, t22=0; double tmin, tmax; bool i1, i2; i1 = intersectCircle(r, t11, t12, ball1pos); i2 = intersectCircle(r, t21, t22, ball2pos); if (!i1 && !i2) return false; else if (!i1 && i2) { tmin = t21; tmax = t22; } else if (i1 && !i2) { tmin = t11; tmax = t12; } else { tmin = min(t11, t21); tmax = max(t12, t22); } for (double t = tmin; t < tmax; t += 0.001) { vec3f point = r.getPosition() + t * r.getDirection(); double value = calvalue(point, ball1pos, ball2pos); if ((!inside && value > threshold) || (inside && value < threshold)) { // prevent fake intersect if (inside && t < 0.01)return false; vec3f normal; normal += 2 * (point - ball1pos) / ((point - ball1pos).length()*(point - ball1pos).length()); normal += 2 * (point - ball2pos) / ((point - ball2pos).length()*(point - ball2pos).length()); normal = normal.normalize(); i.t = t; i.N = normal; i.obj = this; return true; } } return false; }
// Apply the phong model to this point on the surface of the object, returning // the color of that point. vec3f Material::shade( Scene *scene, const ray& r, const isect& i ) const { // YOUR CODE HERE // For now, this method just returns the diffuse color of the object. // This gives a single matte color for every distinct surface in the // scene, and that's it. Simple, but enough to get you started. // (It's also inconsistent with the phong model...) // Your mission is to fill in this method with the rest of the phong // shading model, including the contributions of all the light sources. // You will need to call both distanceAttenuation() and shadowAttenuation() // somewhere in your code in order to compute shadows and light falloff. //intersection point vec3f point = r.at(i.t); bool istransmissive = abs(index - 1.0) > NORMAL_EPSILON || !kt.iszero(); vec3f rate = vec3f(1, 1, 1) - kt; vec3f I = ke; //ambient if (istransmissive) I += scene->ambient.time(ka).time(rate).clamp(); else I += scene->ambient.time(ka).clamp(); list<Light*>::const_iterator begin = scene->beginLights(); list<Light*>::const_iterator end = scene->endLights(); while (begin != end) { vec3f atten = (*begin)->shadowAttenuation(point) * (*begin)->distanceAttenuation(point); vec3f L = (*begin)->getDirection(point); double NL = i.N.dot(L); //diffuse if (istransmissive) I += (atten * NL).time(kd).time(rate).clamp(); else I += (atten * NL).time(kd).clamp(); //specular vec3f R = i.N * (2 * NL) - L; double RV = -R.dot(r.getDirection()); //TODO: where is n£¿ double n = 64; I += (atten * pow(RV, n)).time(ks).clamp(); begin++; } return I; }
// if the ray hits the box, put the "t" value of the intersection // closest to the origin in tMin and the "t" value of the far intersection // in tMax and return true, else return false. // Using Kay/Kajiya algorithm. bool BoundingBox::intersect(const ray& r, double& tMin, double& tMax) const { vec3f R0 = r.getPosition(); vec3f Rd = r.getDirection(); tMin = -1.0e308; // 1.0e308 is close to infinity... close enough for us! tMax = 1.0e308; double ttemp; for (int currentaxis = 0; currentaxis < 3; currentaxis++) { double vd = Rd[currentaxis]; // if the ray is parallel to the face's plane (=0.0) if( vd > -RAY_EPSILON && vd < RAY_EPSILON ) { if(R0[currentaxis] <= min[currentaxis] - RAY_EPSILON || R0[currentaxis] >= max[currentaxis] + RAY_EPSILON) { return false; } else { continue; } } double v1 = min[currentaxis] - R0[currentaxis]; double v2 = max[currentaxis] - R0[currentaxis]; // two slab intersections double t1 = v1/vd; double t2 = v2/vd; if ( t1 > t2 ) { // swap t1 & t2 ttemp = t1; t1 = t2; t2 = ttemp; } if (t1 > tMin) tMin = t1; if (t2 < tMax) tMax = t2; if (tMin > tMax) // box is missed return false; if (tMax < -RAY_EPSILON) // box is behind ray return false; } return true; // it made it past all 3 axes. }
bool Box::intersectLocal( const ray& r, isect& i ) const { BoundingBox bounds = ComputeLocalBoundingBox(); vec3f p = r.getPosition(); vec3f d = r.getDirection(); //find tmin and tmax vec3f tmin; vec3f tmax; vec3f nmin(0, 0, 0); vec3f nmax(0, 0, 0); double min; double max; for(int j=0; j<3; j++) { if(d[j]>=0) { tmin[j] = (bounds.min[j] - p[j]) / d[j]; tmax[j] = (bounds.max[j] - p[j]) / d[j]; nmin[j] = -1; nmax[j] = 1; } else { tmin[j] = (bounds.max[j] - p[j]) / d[j]; tmax[j] = (bounds.min[j] - p[j]) / d[j]; nmin[j] = 1; nmax[j] = -1; } } //min of tmax, max of tmin max = std::min( std::min(tmax[0], tmax[1]), tmax[2]); min = std::max( std::max(tmin[0], tmin[1]), tmin[2]); if(min > max || max < RAY_EPSILON) return false; i.obj = this; vec3f N(0, 0, 0); if(min >= RAY_EPSILON) { i.t = min; for(int i=0; i<3; i++) { if(tmin[i] == min) { N[i] = nmin[i]; break; } } } else { i.t = max; for(int i=0; i<3; i++) { if(tmax[i] == max) { N[i] = nmax[i]; break; } } } i.N = N; return true; }
bool Geometry::intersect(const ray&r, isect&i) const { double tmin, tmax; if (hasBoundingBoxCapability() && !(bounds.intersect(r, tmin, tmax))) return false; // Transform the ray into the object's local coordinate space Vec3d pos = transform->globalToLocalCoords(r.getPosition()); Vec3d dir = transform->globalToLocalCoords(r.getPosition() + r.getDirection()) - pos; double length = dir.length(); dir /= length; ray localRay( pos, dir, r.type() ); if (intersectLocal(localRay, i)) { // Transform the intersection point & normal returned back into global space. i.N = transform->localToGlobalCoordsNormal(i.N); i.t /= length; return true; } else return false; }
bool Geometry::intersect(const ray&r, isect&i) const { // Transform the ray into the object's local coordinate space vec3f pos = transform->globalToLocalCoords(r.getPosition()); vec3f dir = transform->globalToLocalCoords(r.getPosition() + r.getDirection()) - pos; double length = dir.length(); dir /= length; ray localRay( pos, dir ); if (intersectLocal(localRay, i)) { // Transform the intersection point & normal returned back into global space. i.N = transform->localToGlobalCoordsNormal(i.N); i.t /= length; return true; } else { return false; } }
//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; }
vec3f PointLight::_shadowAttenuation(const vec3f& P, const ray& r) const { double distance = (position - P).length(); vec3f d = r.getDirection(); vec3f result = getColor(P); vec3f curP = r.getPosition(); isect isecP; ray newr(curP, d); while (scene->intersect(newr, isecP)) { //prevent going beyond this light if ((distance -= isecP.t) < RAY_EPSILON) return result; //if not transparent return black if (isecP.getMaterial().kt.iszero()) return vec3f(0, 0, 0); //use current intersection point as new light source curP = r.at(isecP.t); newr = ray(curP, d); result = prod(result, isecP.getMaterial().kt); } return result; }
//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; }
bool Cone::intersectCaps( const ray& r, isect& i ) const { if( !capped ) { return false; } double pz = r.getPosition()[2]; double dz = r.getDirection()[2]; if( 0.0 == dz ) { return false; } double t1; double t2; double r1; double r2; if( dz > 0.0 ) { t1 = (-pz)/dz; t2 = (height-pz)/dz; r1 = b_radius; r2 = t_radius; } else { t1 = (height-pz)/dz; t2 = (-pz)/dz; r1 = t_radius; r2 = b_radius; } if( t2 < RAY_EPSILON ) { return false; } if( t1 >= RAY_EPSILON ) { vec3f p( r.at( t1 ) ); if( (p[0]*p[0] + p[1]*p[1]) <= r1 * r1 ) { i.t = t1; if( dz > 0.0 ) { // Intersection with cap at z = 0. i.N = vec3f( 0.0, 0.0, -1.0 ); } else { i.N = vec3f( 0.0, 0.0, 1.0 ); } return true; } } vec3f p( r.at( t2 ) ); if( (p[0]*p[0] + p[1]*p[1]) <= r2 * r2 ) { i.t = t2; if( dz > 0.0 ) { // Intersection with interior of cap at z = 1. i.N = vec3f( 0.0, 0.0, 1.0 ); } else { i.N = vec3f( 0.0, 0.0, -1.0 ); } return true; } return false; }
// Do recursive ray tracing! You'll want to insert a lot of code here // (or places called from here) to handle reflection, refraction, etc etc. Vec3d RayTracer::traceRay( const ray& r, const Vec3d& thresh, int depth ) { isect i; if( 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(); Vec3d ret = m.shade(scene, r, i); if (depth == traceUI->getDepth()) return ret; Vec3d d = r.getDirection(); double n; Vec3d NN = i.N; if (i.N * (-d) >= 0) { // entering the object n = 1.0 / m.index(i); } else { // leaving n = m.index(i); NN = -NN; } double c1 = -(NN * d); Vec3d Rl = d + 2 * NN * c1; ray reflection = ray(r.at(i.t), Rl, r.REFLECTION); ret += prod(m.kr(i), traceRay(reflection, thresh, depth+1)); double c2 = 1 - n*n * (1 - c1*c1); if (c2 >= 0) { // not total internal reflection c2 = sqrt(c2); Vec3d Rr = (n * d) + (n * c1 - c2) * NN; ray refraction = ray(r.at(i.t), Rr, r.REFRACTION); ret += prod(m.kt(i), traceRay(refraction, thresh, depth+1)); } return ret; } 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. return Vec3d( 0.0, 0.0, 0.0 ); } }
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; }
// Do recursive ray tracing! You'll want to insert a lot of code here // (or places called from here) to handle reflection, refraction, etc etc. Vec3d RayTracer::traceRay( const ray& r, const Vec3d& thresh, int depth ) { if (depth > traceUI->getDepth()) return Vec3d( 0.0, 0.0, 0.0 ); ++depth; isect i; if( 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(); Vec3d iPoint = r.at(i.t); // point of intersection /* // --Shadows-- Vec3d shadow = Vec3d(1.0, 1.0, 1.0); //for (vector<Light*>::const_iterator litr = scene->beginLights(); // litr != scene->endLights(); ++litr ) //{ vector<Light*>::const_iterator litr = scene->beginLights(); Light* pLight = *litr; Vec3d shadowDir = pLight->getDirection(iPoint); isect aux; ray shadow_ray (iPoint,shadowDir,ray::SHADOW); if(scene->intersect(shadow_ray, aux)) { const Material& mm = aux.getMaterial(); shadow = prod(shadow, mm.kt(aux)); } //} */ // --Reflection-- Vec3d reflection = Vec3d(0.0,0.0,0.0); if (m.kr(i) != Vec3d(0.0,0.0,0.0)) { Vec3d reflecDir = 2*((-1*r.getDirection()*i.N)*i.N) + r.getDirection(); reflecDir.normalize(); ray ReflRay (iPoint, reflecDir, ray::REFLECTION); reflection = traceRay(ReflRay, Vec3d(1.0,1.0,1.0), depth); // use threshold for(int i=0; i < 3; i++){ if(reflection[i] > thresh[i]){ reflection[i] = 1; } } } // --Refraction-- Vec3d refraction = Vec3d(0.0,0.0,0.0); if (m.kt(i) != Vec3d(0.0,0.0,0.0)) { double IofR = 1.0 / m.index(i); // the index of refraction Vec3d inorm = i.N; Vec3d d = -1.0*r.getDirection(); // reverse the ray direction for calculations double cos_i = inorm*d; // if the ray is inside of the object if (cos_i < 0.0) { IofR = m.index(i); cos_i *= -1.0; inorm *= -1.0; } double cos_t = 1.0 - (IofR*IofR)*(1.0 - (cos_i*cos_i)); // if not Total Internal Reflection if (cos_t >= 0.0) { Vec3d refractDir = (IofR*cos_i - sqrt(cos_t))*inorm - IofR*d; refractDir.normalize(); ray RefrRay (iPoint, refractDir, ray::REFRACTION); refraction = traceRay(RefrRay, Vec3d(1.0,1.0,1.0), depth); // use threshold for(int i=0; i < 3; i++){ if(refraction[i] > thresh[i]){ refraction[i] = 1; } } } } return m.shade(scene, r, i) + prod(m.kr(i), reflection) + prod(m.kt(i), refraction); } 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. return Vec3d( 0.0, 0.0, 0.0 ); } }
Vec3d CubeMap::getColor(ray r) const { int axis, front, left, right, top, bottom; double u,v; Vec3d dir = r.getDirection(); if (fabs(dir[0]) > fabs(dir[1])) if (fabs(dir[0]) > fabs(dir[2])) { axis = 0; bottom = 3; top = 2; if (dir[0] > 0.0) { front = 0; left = 4; right = 5; } else { front = 1; left = 5; right = 4; } } else { axis = 2; bottom = 3; top = 2; if (dir[2] > 0.0) { front = 5; left = 0; right = 1; } else { front = 4; left = 1; right = 0; } } else if (fabs(dir[1]) > fabs(dir[2])) { axis = 1; left = 1; right = 0; if (dir[1] > 0.0) { front = 2; bottom = 4; top = 5; } else { front = 3; bottom = 5; top = 4; } } else { axis = 2; bottom = 3; top = 2; if (dir[2] > 0.0) { front = 5; left = 0; right = 1; } else { front = 4; left = 1; right = 0; } } if (axis == 0) { u = dir[2]/dir[0]; if (dir[0] > 0.0) v = dir[1]/dir[0]; else v = -dir[1]/dir[0]; } else if (axis == 1) { if (dir[1] > 0.0) u = dir[0]/dir[1]; else u = -dir[0]/dir[1]; v = dir[2]/dir[1]; } else if (axis == 2) { u = -dir[0]/dir[2]; if (dir[2] > 0.0) v = dir[1]/dir[2]; else v = -dir[1]/dir[2]; } u = (u + 1.0)/2.0; v = (v + 1.0)/2.0; int filterwidth = traceUI->getFilterWidth(); if (filterwidth == 1) return tMap[front]->getMappedValue(Vec2d(u, v)); // Why even bother with expensive computation when we can grab straight from the image? int fw = (filterwidth + 1)/2 - 1; int rm = filterwidth - fw; int width = tMap[front]->getWidth(); int height = tMap[front]->getHeight(); double delu = 1.0/width; double delv = 1.0/height; int x = floor(0.5 + u * (width - 1)); int y = floor(0.5 + v * (height - 1)); double startu = delu * (x - fw); double startv = delv * (y - fw); double nextu = startu; double nextv = startv; double xslope = 1.0/(((double)filterwidth + 1) / 2.0 * delu); double yslope = 1.0/(((double)filterwidth + 1) / 2.0 * delv); double correct = 0.0; Vec3d thePixel = Vec3d(0.0, 0.0, 0.0); for (int jj = -fw; jj < rm; jj++) { for (int ii = -fw; ii < rm; ii++) { int xindex = x + ii; int yindex = y + jj; if (xindex < 0 && yindex < 0) continue; if (xindex >= width && yindex >= height) continue; if (xindex >= width && yindex < 0) continue; if (xindex < 0 && yindex >= height) continue; int theMap = front; if (yindex < 0) { theMap = bottom; if (axis == 0) if (dir[0] > 0) { yindex = height - 1 - xindex; xindex = width + jj; } else { yindex = xindex; xindex = -1 - jj; } else if (axis == 1) if (dir[1] > 0) yindex = height + jj; else { yindex = -1 - jj; xindex = width - 1 - xindex; } else if (axis == 2) if (dir[2] > 0) { yindex = -1 - jj; xindex = width - 1 - xindex; } else yindex = height + jj; } else if (yindex >= height) { theMap = top; if (axis == 0) if (dir[0] > 0) { yindex = xindex; xindex = width - jj; } else { yindex = height - 1 - xindex; xindex = jj - 1; } else if (axis == 1) if (dir[1] > 0) { yindex = height - jj; xindex = width - 1 - xindex; } else yindex = jj - 1; else if (axis == 2) if (dir[2] > 0) { yindex = height - jj; xindex = width - 1 - xindex; } else yindex = jj - 1; } else if (xindex < 0) { theMap = left; if (axis == 0 || axis == 2) xindex = width + ii; else if (axis == 1) if (dir[1] > 0) { xindex = width - 1 - yindex; yindex = height + ii; } else { xindex = yindex; yindex = -1 - ii; } } else if (xindex >= width) { theMap = right; if (axis == 0 || axis == 2) xindex = ii - 1; else if (axis == 1) if (dir[1] > 0) { xindex = yindex; yindex = height - ii; } else { xindex = width - 1 - yindex; yindex = ii - 1; } } double du = u - nextu; double dv = v - nextv; double weight = (1.0 - fabs(du * xslope)) * (1.0 - fabs(dv * yslope)); nextu += delu; correct += weight; thePixel += tMap[theMap]->getPixelAt(xindex, yindex) * weight; } nextu = startu; nextv += delv; } thePixel /= correct; return thePixel; }
// 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 { // YOUR CODE HERE // For now, this method just returns the diffuse color of the object. // This gives a single matte color for every distinct surface in the // scene, and that's it. Simple, but enough to get you started. // (It's also inconsistent with the phong model...) // Your mission is to fill in this method with the rest of the phong // shading model, including the contributions of all the light sources. // You will need to call both distanceAttenuation() and shadowAttenuation() // somewhere in your code in order to compute shadows and light falloff. if( debugMode ) std::cout << "Debugging Phong code..." << std::endl; Vec3d intensity(0.0f,0.0f,0.0f); Vec3d emmisiveIntensity = ke(i); Vec3d ambientIntensity = ka(i); ambientIntensity %= scene->ambient(); // When you're iterating through the lights, // you'll want to use code that looks something // like this: // const Vec3d intersectionPoint = r.at(i.t); const Vec3d blue(0.0f,0.0f,0.4f); const Vec3d yellow(0.4f,0.4f,0.0f); double alpha = 0.2f; double beta = 0.6f; bool bNonRealism = traceUI->nonRealism(); for ( vector<Light*>::const_iterator litr = scene->beginLights(); litr != scene->endLights(); ++litr ){ Light* pLight = *litr; Vec3d lightDirection = pLight->getDirection(intersectionPoint); Vec3d surfaceNormal = i.N; surfaceNormal.normalize(); double aLightToNormal = surfaceNormal * lightDirection; Vec3d lightReflectedDirectionCi = (lightDirection*surfaceNormal)*surfaceNormal; Vec3d lightReflectedDirectionSi = lightReflectedDirectionCi - lightDirection; Vec3d lightReflectedDirection = lightReflectedDirectionCi + lightReflectedDirectionSi; lightReflectedDirection.normalize(); double aRayToLight = (-1 * r.getDirection()) * lightReflectedDirection; Vec3d shadowLight(1.0f,1.0f,1.0f); if(bNonRealism){ if(bump(i)[0]!= 2.0f){ Vec3d perturbed = 2.0f*bump(i)-1.0f; perturbed[0] *= traceUI->getBumpScale(); perturbed[1] *= traceUI->getBumpScale(); perturbed %=surfaceNormal; perturbed.normalize(); aLightToNormal = perturbed * lightDirection;} double coolFac = (1.0f+ (-1.0f)* aLightToNormal)/2.0f; double warmFac = 1.0f - coolFac; Vec3d diffuseIntensity = (coolFac * ( blue + kd(i) * alpha)) + (warmFac * ( yellow + kd(i) * beta)); //Vec3d specularIntensity = ks(i) * pLight->getColor(intersectionPoint); shadowLight %= diffuseIntensity; //+ specularIntensity * std::pow(std::max(aRayToLight,0.0),shininess(i))); } else { if(bump(i)[0]!= 2.0f){ Vec3d perturbed = 2.0f*bump(i)-1.0f; perturbed[0] *= traceUI->getBumpScale(); perturbed[1] *= traceUI->getBumpScale(); perturbed %=surfaceNormal; perturbed.normalize(); aLightToNormal = perturbed * lightDirection;} Vec3d diffuseIntensity = kd(i); Vec3d specularIntensity = ks(i); diffuseIntensity%=pLight->getColor(intersectionPoint); specularIntensity%=pLight->getColor(intersectionPoint); shadowLight = pLight->shadowAttenuation(intersectionPoint); shadowLight %= (diffuseIntensity * std::max(aLightToNormal, 0.0) + specularIntensity * std::pow(std::max(aRayToLight,0.0),shininess(i))); } intensity += shadowLight*pLight->distanceAttenuation(intersectionPoint); } return intensity + emmisiveIntensity + ambientIntensity; }
// 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, bool isInAir ) const { // YOUR CODE HERE // For now, this method just returns the diffuse color of the object. // This gives a single matte color for every distinct surface in the // scene, and that's it. Simple, but enough to get you started. // (It's also inconsistent with the Phong model...) // Your mission is to fill in this method with the rest of the phong // shading model, including the contributions of all the light sources. // You will need to call both distanceAttenuation() and shadowAttenuation() // somewhere in your code in order to compute shadows and light falloff. // When you're iterating through the lights, // you'll want to use code that looks something // like this: // // for ( vector<Light*>::const_iterator litr = scene->beginLights(); // litr != scene->endLights(); // ++litr ) // { // Light* pLight = *litr; // . // . // . // } const Vec3d point = r.at(i.t); // Start with emitted light. Vec3d shade = ke(i); // Add ambient light effects. Vec3d ambient = prod(ka(i), scene->ambient()); if (!isInAir) { // If shading a point inside an object, ambient light had to pass through the object first. // So apply the transmission factor to the ambient light to account for object's affect on light. ambient = prod(kt(i), ambient); } shade += ambient; // For each light source... for (vector<Light*>::const_iterator literator = scene->beginLights(); literator != scene->endLights(); ++literator) { const Light* light = *literator; const Vec3d lightDirection = light->getDirection(point); const Vec3d shadowAttenuation = light->shadowAttenuation(point); const double distanceAttenuation = light->distanceAttenuation(point); const Vec3d intensity = prod(light->getColor(point), shadowAttenuation * distanceAttenuation); if (debugMode) cerr << "Shadow: " << shadowAttenuation << ", Distance: " << distanceAttenuation << endl; // Calculate diffuse term. const Vec3d diffuseTerm = kd(i) * max(0.0, i.N * lightDirection); // Calculate specular term. const Vec3d reflectionDir = 2.0 * (lightDirection * i.N) * i.N - lightDirection; const Vec3d viewingDir = -r.getDirection(); const Vec3d specularTerm = ks(i) * pow(max(0.0, viewingDir * reflectionDir), shininess(i)); // Apply diffuse and specular terms to light source. shade += prod(intensity, diffuseTerm + specularTerm); if (debugMode) cerr << "Diffuse: " << diffuseTerm << ", Specular: " << specularTerm << endl; } return shade; }