void R3MeshSearchTree:: FindIntersection(const R3Ray& ray, R3MeshIntersection& closest, RNScalar min_t, RNScalar& max_t, int (*IsCompatible)(const R3Point&, const R3Vector&, R3Mesh *, R3MeshFace *, void *), void *compatible_data, R3MeshFace *face) const { // Check compatibility if (IsCompatible) { if (!(*IsCompatible)(ray.Start(), ray.Vector(), mesh, face, compatible_data)) return; } // Check intersection with plane (this is redundant, but allows checking min_t and max_t) RNScalar plane_t; if (!R3Intersects(ray, mesh->FacePlane(face), NULL, &plane_t)) return; if (plane_t >= max_t) return; if (plane_t < min_t) return; // Check intersection with face R3MeshIntersection face_intersection; if (!mesh->Intersection(ray, face, &face_intersection)) return; if (face_intersection.t >= max_t) return; if (face_intersection.t < min_t) return; // Update closest intersection closest.type = R3_MESH_FACE_TYPE; closest.face = face; closest.point = face_intersection.point; closest.t = face_intersection.t; max_t = face_intersection.t; }
RNBoolean R3Contains(const R3Ray& ray, const R3Box& box) { // Return whether ray contains box if (!R3Contains(ray.Line(), box)) return FALSE; RNOctant octant = ray.Vector().Octant(); if (!R3Contains(ray, box.Corner(~octant & 0x7))) return FALSE; return TRUE; }
// compute intersection between a sphere and a ray R3Intersection ComputeIntersection(R3Sphere *sphere, R3Ray &ray) { R3Intersection i; bool in_front_of_center = false; bool internal = false; R3Vector l = sphere->Center() - ray.Start(); double tca = l.Dot(ray.Vector()); if (tca < 0) // case where ray originates within sphere and points away from its center { tca = -tca; in_front_of_center = true; internal = true; } double d2 = l.Dot(l) - tca * tca; double r2 = sphere->Radius() * sphere->Radius(); if (d2 > r2) // case where ray misses sphere entirely { i.hit = false; return i; } double thc = sqrt(r2 - d2); double t; if (!in_front_of_center) { t = tca - thc; if (t < 0) { t = tca + thc; internal = true; } } else { t = thc - tca; } if (t < 0) { i.hit = false; return i; } R3Point p = ray.Point(t); R3Vector n = p - sphere->Center(); n.Normalize(); if (internal) n = -n; // populate intersection data i.hit = true; i.normal = n; i.position = p; i.t = t; return i; }
static void DrawText(const R2Point& p, const char *s) { // Draw text string s and position p R3Ray ray = viewer->WorldRay((int) p[0], (int) p[1]); R3Point position = ray.Point(2 * viewer->Camera().Near()); glRasterPos3d(position[0], position[1], position[2]); while (*s) glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *(s++)); }
double R3Distance(const R3Point& point, const R3Ray& ray) { // Check if start point is closest R3Vector v = point - ray.Start(); double dir = v.Dot(ray.Vector()); if (dir < 0) return v.Length(); // Return distance from point to ray line return R3Distance(point, ray.Line()); }
int IntersectPlane(R3Plane *p, R3Ray r, R3Point *position, R3Vector *normal, double *t) { double denom = r.Vector().Dot(p->Normal()); if(denom == 0) { return 0; } else { *t = - (r.Start().Vector().Dot(p->Normal()) + p->D()) / denom; *position = r.Start() + *t * r.Vector(); *normal = p->Normal(); return 1; } }
// compute the ray reflected from the incoming ray r at intersection i R3Ray GetReflectedRay(R3Intersection i, R3Ray r) { // add offset to point to ensure the same intersection is not found again due to floating point error R3Point offset = i.position + i.normal * EPSILON; R3Vector v = -r.Vector(); return R3Ray(offset, 2 * v.Dot(i.normal) * i.normal - v); }
int IntersectMesh(R3Mesh *m, R3Ray r, R3Point *position, R3Vector *normal, double *t) { *t = DBL_MAX; for(int i=0; i < m->NFaces(); i++) { R3MeshFace *f = m->Face(i); if(f->vertices.size() != 3) continue; R3Vector trianglenormal = (f->vertices[1]->position - f->vertices[0]->position); trianglenormal.Cross(f->vertices[2]->position - f->vertices[0]->position); trianglenormal.Normalize(); R3Plane triangleplane(f->vertices[0]->position, trianglenormal); R3Point intersectionpoint; double t_intersection; if(IntersectPlane(&triangleplane, r, &intersectionpoint, &trianglenormal, &t_intersection) !=0 ) { // check inside triangle int withintriangle = 1; for(int j=0; j<3; j++) { R3Vector v1 = f->vertices[j%3]->position - r.Start(); R3Vector v2 = f->vertices[(j+1)%3]->position - r.Start(); R3Vector n1 = v2; n1.Cross(v1); n1.Normalize(); R3Plane p(r.Start(), n1); if(R3SignedDistance(p, intersectionpoint) < 0) { withintriangle = 0; break; } } if(withintriangle == 1 && t_intersection > 0 && t_intersection < *t) { *t = t_intersection; *position = intersectionpoint; *normal = trianglenormal; } } } if(*t < DBL_MAX) { return 1; } else { return 0; } }
double R3SignedDistance(const R3Plane& plane, const R3Ray& ray) { // Return signed distance from plane to ray double d1 = R3SignedDistance(plane, ray.Start()); if (d1 > 0) { // Start point is above plane double dot = ray.Vector().Dot(plane.Normal()); if (dot < 0) return 0.0; else return d1; } else if (d1 < 0) { // Start point is below plane double dot = ray.Vector().Dot(plane.Normal()); if (dot > 0) return 0.0; else return d1; } else { // Start point is on plane return 0.0; } }
int IntersectSphere(R3Sphere *s, R3Ray r, R3Point *position, R3Vector *normal, double *t) { R3Vector l = s->Center() - r.Start(); double tca = l.Dot(r.Vector()); if(tca < 0) return 0; double d2 = l.Dot(l) - tca*tca; if(d2 > s->Radius() * s->Radius()) return 0; double thc = sqrt(s->Radius() * s->Radius() - d2); *t = thc > 0 ? tca - thc : tca + thc; if(*t < 0) { *t = thc > 0 ? tca + thc : tca - thc; } if(*t < 0) { return 0; } *position = r.Start() + *t * r.Vector(); *normal = *position - s->Center(); normal->Normalize(); return 1; }
double R3Distance(const R3Ray& ray, const R3Segment& segment) { // There's got to be a better way ??? // Get vectors in more convenient form const R3Vector v1 = ray.Vector(); const R3Vector v2 = segment.Vector(); // Compute useful intermediate values const double v1v1 = 1.0; // v1.Dot(v1); const double v2v2 = 1.0; // v2.Dot(v2); double v1v2 = v1.Dot(v2); double denom = v1v2*v1v2 - v1v1*v2v2; // Check if ray and segment are parallel if (denom == 0) { // Not right ??? // Look at directions of vectors, then check relative starts and stops return R3Distance(segment.Line(), ray.Line()); } else { // Find closest points const R3Vector p1 = ray.Start().Vector(); const R3Vector p2 = segment.Start().Vector(); double p1v1 = v1.Dot(p1); double p2v2 = v2.Dot(p2); double p1v2 = v2.Dot(p1); double p2v1 = v1.Dot(p2); double ray_t = (v1v2*p2v2 + v2v2*p1v1 - v1v2*p1v2 - v2v2*p2v1) / denom; double segment_t = (v1v2*p1v1 + v1v1*p2v2 - v1v2*p2v1 - v1v1*p1v2) / denom; R3Point ray_point = (ray_t <= 0.0) ? ray.Start() : ray.Point(ray_t); R3Point segment_point = (segment_t <= 0.0) ? segment.Start() : (segment_t >= segment.Length()) ? segment.End() : segment.Ray().Point(segment_t); double distance = R3Distance(ray_point, segment_point); return distance; } }
// compute ray refracted from incoming ray r using Snell's Law // NOTE: I assume that refracting objects will always be non-intersecting and that all intersections passed into this function // will be from empty space with an IOR of 1 to the interior of a shape R3Ray GetRefractedRay(R3Intersection i, R3Ray r, double ior, R3Scene *scene) { // this time push the point inside the object R3Point internal_offset = i.position - i.normal * EPSILON; // calculate the ray that will be cast through the object R3Vector v = -r.Vector(); double cos_vn = v.Dot(i.normal); R3Vector internal_vec = i.normal * (cos_vn / ior - sqrt(1 - (1 - cos_vn * cos_vn) / (ior * ior))) - v / ior; R3Ray trans_ray(internal_offset, internal_vec); // cast this ray and determine it's exit point from the refracting object R3Intersection exit_i = ComputeIntersection(scene, scene->Root(), trans_ray, numeric_limits<double>::infinity()); assert (exit_i.hit); // calculate the refracted exit ray and R3Point external_offset = exit_i.position - exit_i.normal * EPSILON; v = -trans_ray.Vector(); cos_vn = v.Dot(exit_i.normal); R3Vector external_vec = exit_i.normal * (cos_vn * ior - sqrt(1 - (1 - cos_vn * cos_vn) * (ior * ior))) - v * ior; return R3Ray(external_offset, external_vec); }
RNBoolean R3Parallel(const R3Ray& ray, const R3Plane& plane) { // Return whether ray and plane are parallel return R3Perpendicular(ray.Vector(), plane.Normal()); }
RNBoolean R3Perpendicular(const R3Ray& ray1, const R3Ray& ray2) { // Return whether ray1 and ray2 are perpendicular return R3Perpendicular(ray1.Vector(), ray2.Vector()); }
RNBoolean R3Perpendicular(const R3Ray& ray, const R3Span& span) { // Return whether ray and span are perpendicular return R3Perpendicular(ray.Vector(), span.Vector()); }
RNBoolean R3Perpendicular(const R3Vector& vector, const R3Ray& ray) { // Return whether vector and ray are perpendicular return R3Perpendicular(vector, ray.Vector()); }
RNBoolean R3Perpendicular(const R3Line& line, const R3Ray& ray) { // Return whether line and ray are perpendicular return R3Perpendicular(line.Vector(), ray.Vector()); }
// compute intersection between a triangle face and a ray R3Intersection ComputeIntersection(R3MeshFace *tri, R3Ray &ray, double min_t) { assert (tri->vertices.size() == 3); R3Intersection i; R3Point t1 = tri->vertices[0]->position; R3Point t2 = tri->vertices[1]->position; R3Point t3 = tri->vertices[2]->position; // intersect ray with triangle's plane R3Plane plane = tri->plane; double t = -(ray.Start().Vector().Dot(plane.Normal()) + plane.D()) / (ray.Vector().Dot(plane.Normal())); // early return if not closer than minimum intersection for the mesh if (t > min_t || t < 0) { i.hit = false; return i; } // check if intersection is within triangle using barycentric coordinate method R3Point p = ray.Point(t); R3Vector v; v = t2 - t1; v.Cross(t3 - t1); double area = v.Length() / 2; v = t2 - t1; v.Cross(p - t1); if (v.Dot(plane.Normal()) < 0) { i.hit = false; return i; } double a = v.Length() / (2 * area); v = p - t1; v.Cross(t3 - t1); if (v.Dot(plane.Normal()) < 0) { i.hit = false; return i; } double b = v.Length() / (2 * area); if (a <= 1 && a >= 0 && b <= 1 && b >= 0 && a + b <= 1) { i.hit = true; if (ray.Vector().Dot(plane.Normal()) < 0) { i.normal = plane.Normal(); } else { i.normal = -plane.Normal(); } i.position = p; i.t = t; return i; } else { i.hit = false; return i; } }
// return point of intersection between ray and plane R3Point RayPlaneIntersection(R3Plane plane, R3Ray ray) { double t = -(ray.Start().Vector().Dot(plane.Normal()) + plane.D()) / (ray.Vector().Dot(plane.Normal())); return ray.Point(t); }
int IntersectCone(R3Cone *c, R3Ray r, R3Point *position, R3Vector *normal, double *t) { R3Vector v = r.Vector(); R3Point s = r.Start(); *t = DBL_MAX; double k = pow(c->Radius() / c->Height(), 2.0); double y_apex = c->Center()[1] + c->Height() / 2.0; // check cone side double eqa = v[0]*v[0] + v[2]*v[2] - k*v[1]*v[1]; double eqb = 2.0*v[0]*(s[0] - c->Center()[0]) + 2.0*v[2]*(s[2] - c->Center()[2]) - 2.0*k*v[1]*(s[1] - y_apex); double eqc = pow(s[0] - c->Center()[0], 2.0) + pow(s[2] - c->Center()[2], 2.0) - k*pow(s[1] - y_apex, 2.0); double t1, t2; t1 = (-eqb + sqrt(eqb*eqb - 4.0*eqa*eqc))/2.0/eqa; t2 = (-eqb - sqrt(eqb*eqb - 4.0*eqa*eqc))/2.0/eqa; R3Point possible1, possible2; possible1 = r.Start() + r.Vector()*t1; possible2 = r.Start() + r.Vector()*t2; // Check if in y range if(possible1[1] < c->Center()[1] - c->Height()/2.0 || possible1[1] > c->Center()[1] + c->Height()/2.0) { t1 = -1; } if(possible2[1] < c->Center()[1] - c->Height()/2.0 || possible2[1] > c->Center()[1] + c->Height()/2.0) { t2 = -1; } if(t1 > 0 && t2 > 0) { if(t1 > t2) { *t = t2; *position = possible2; *normal = R3Vector(possible2[0] - c->Center()[0], 0, possible2[2] - c->Center()[2]); normal->Normalize(); (*normal)[1] = k; } else { *t = t1; *position = possible1; *normal = R3Vector(possible1[0] - c->Center()[0], 0, possible1[2] - c->Center()[2]); normal->Normalize(); (*normal)[1] = k; } } else if(t1 > 0) { *t = t1; *position = possible1; *normal = R3Vector(possible1[0] - c->Center()[0], 0, possible1[2] - c->Center()[2]); normal->Normalize(); (*normal)[1] = k; } else if(t2 > 0) { *t = t2; *position = possible2; *normal = R3Vector(possible2[0] - c->Center()[0], 0, possible2[2] - c->Center()[2]); normal->Normalize(); (*normal)[1] = k; } //Check endcaps R3Vector norm_y(0, -1, 0); R3Point planepoint(c->Center()[0], c->Center()[1] - c->Height()/2.0, c->Center()[2]); R3Plane plane(planepoint, norm_y); R3Point endcap_p; double endcap_t; if(IntersectPlane(&plane, r, &endcap_p, &norm_y, &endcap_t) == 1) { if(pow(endcap_p[0] - c->Center()[0], 2.0) + pow(endcap_p[2] - c->Center()[2], 2.0) < c->Radius()*c->Radius() && endcap_t > 0) { if(endcap_t < *t) { *t = endcap_t; *position = endcap_p; *normal = norm_y; } } } if(*t < DBL_MAX) { return 1; } else { return 0; } }
// compute intersection between a box and a ray R3Intersection ComputeIntersection(R3Box *box, R3Ray &ray) { R3Intersection min_intersection; min_intersection.t = numeric_limits<double>::infinity(); min_intersection.hit = false; // check cases where: // 1) ray originates on the positive side of the max and points negative // 2) ray originates on the negative side of the min and points positive // 3) ray originates within the box and points either way // for each of X, Y and Z // I wrote the function out like this to eliminate most unnecessary checks that would // come along with a "cleaner" implementation. This tradeoff between optimization // and clean code is a good one because this intersection function is used for bounding boxes, // a critical part of ray intersection. // X DIRECTION if (ray.Start().X() <= box->XMin()) { if (ray.Vector().X() > 0) { double t = (box->XMin() - ray.Start().X()) / ray.Vector().X(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxX(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(-1, 0, 0); min_intersection.position = p; min_intersection.t = t; } } else return min_intersection; } else if (ray.Start().X() >= box->XMax()) { if (ray.Vector().X() < 0) { double t = (box->XMax() - ray.Start().X()) / ray.Vector().X(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxX(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(1, 0, 0); min_intersection.position = p; min_intersection.t = t; } } else return min_intersection; } else { if (ray.Vector().X() > 0) { double t = (box->XMax() - ray.Start().X()) / ray.Vector().X(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxX(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(-1, 0, 0); min_intersection.position = p; min_intersection.t = t; } } else if (ray.Vector().X() < 0) { double t = (box->XMin() - ray.Start().X()) / ray.Vector().X(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxX(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(1, 0, 0); min_intersection.position = p; min_intersection.t = t; } } } // Y DIRECTION if (ray.Start().Y() <= box->YMin()) { if (ray.Vector().Y() > 0) { double t = (box->YMin() - ray.Start().Y()) / ray.Vector().Y(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxY(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, -1, 0); min_intersection.position = p; min_intersection.t = t; } } else return min_intersection; } else if (ray.Start().Y() >= box->YMax()) { if (ray.Vector().Y() < 0) { double t = (box->YMax() - ray.Start().Y()) / ray.Vector().Y(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxY(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, 1, 0); min_intersection.position = p; min_intersection.t = t; } } else return min_intersection; } else { if (ray.Vector().Y() > 0) { double t = (box->YMax() - ray.Start().Y()) / ray.Vector().Y(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxY(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, -1, 0); min_intersection.position = p; min_intersection.t = t; } } else if (ray.Vector().Y() < 0) { double t = (box->YMin() - ray.Start().Y()) / ray.Vector().Y(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxY(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, 1, 0); min_intersection.position = p; min_intersection.t = t; } } } // Z DIRECTION if (ray.Start().Z() <= box->ZMin()) { if (ray.Vector().Z() > 0) { double t = (box->ZMin() - ray.Start().Z()) / ray.Vector().Z(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxZ(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, 0, -1); min_intersection.position = p; min_intersection.t = t; } } else return min_intersection; } else if (ray.Start().Z() >= box->ZMax()) { if (ray.Vector().Z() < 0) { double t = (box->ZMax() - ray.Start().Z()) / ray.Vector().Z(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxZ(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, 0, 1); min_intersection.position = p; min_intersection.t = t; } } else return min_intersection; } else { if (ray.Vector().Z() > 0) { double t = (box->ZMax() - ray.Start().Z()) / ray.Vector().Z(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxZ(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, 0, -1); min_intersection.position = p; min_intersection.t = t; } } else if (ray.Vector().Z() < 0) { double t = (box->ZMin() - ray.Start().Z()) / ray.Vector().Z(); R3Point p = ray.Point(t); if (t >= 0 && t < min_intersection.t && CheckBoxZ(p, box)) { min_intersection.hit = true; min_intersection.normal = R3Vector(0, 0, 1); min_intersection.position = p; min_intersection.t = t; } } } return min_intersection; }
// compute the nearest intersection between a ray and a scene node R3Intersection ComputeIntersection(R3Scene *scene, R3Node *node, R3Ray ray, double min_t) { // transform the ray and minimum t from parent node coordinates to this node's coordinates R3Vector v_orig = ray.Vector(); v_orig.InverseTransform(node->transformation); double min_t_trans = min_t * v_orig.Length(); ray.InverseTransform(node->transformation); // initialize intersection as infinitely far away R3Intersection min_intersection; min_intersection.hit = false; min_intersection.t = min_t_trans; // check this node if it contains a shape if (node->shape != NULL && node->is_obstacle) { R3Intersection i; i.hit = false; i.t = numeric_limits<double>::infinity(); // intersect with node shape switch (node->shape->type) { case R3_BOX_SHAPE: { i = ComputeIntersection(node->shape->box, ray); } break; case R3_SPHERE_SHAPE: { i = ComputeIntersection(node->shape->sphere, ray); } break; case R3_MESH_SHAPE: { i = ComputeIntersection(node->shape->mesh, ray, min_intersection.t); } break; // not implemented case R3_COIN_SHAPE: case R3_CYLINDER_SHAPE: i = ComputeIntersection(node->shape->cylinder, ray); case R3_CONE_SHAPE: break; case R3_SEGMENT_SHAPE: break; case R3_CIRCLE_SHAPE: break; case R3_NUM_SHAPE_TYPES: break; } if (node->is_coin) { printf("I'm a coin"); i = ComputeIntersection(&(node->bbox), ray); } // update minimum if found if (i.hit && i.t < min_intersection.t) { i.node = node; min_intersection = i; } } // recursively intersect to children nodes for (unsigned int i = 0; i < node->children.size(); i++) { // check if ray intersects with the child node R3Intersection child_bbox_intersection = ComputeIntersection(&(node->children[i]->bbox), ray); // if true, intersect with this child node if (child_bbox_intersection.hit && child_bbox_intersection.t < min_intersection.t) { R3Intersection child_intersection = ComputeIntersection(scene, node->children[i], ray, min_intersection.t); // update intersection if necessary if (child_intersection.hit && child_intersection.t < min_intersection.t) { min_intersection = child_intersection; } } } // transform intersection from this node's coordinates to the parent node coordinates if (min_intersection.hit) { min_intersection.normal.InverseTransform(node->transformation.Transpose()); min_intersection.normal.Normalize(); min_intersection.position.Transform(node->transformation); R3Vector v2 = ray.Vector(); v2.Transform(node->transformation); min_intersection.t = min_intersection.t * v2.Length(); } return min_intersection; }
int IntersectNode(R3Node *node, R3Ray r, R3Point *position, R3Vector *normal, double *t, R3Node **intersectingnode, R3Node *excludenode) { *t = DBL_MAX; R3Ray orig_r = r; R3Point intersectionpoint; R3Vector intersectionnormal; double t_intersection; R3Matrix tmatrix = node->transformation; R3Matrix tinvmatrix = tmatrix.Inverse(); r.Transform(tinvmatrix); if(node->shape != NULL && excludenode != node) { R3Shape *shape = node->shape; int intersects = -1; if(shape->type == R3_SPHERE_SHAPE) { R3Sphere *s = shape->sphere; intersects = IntersectSphere(s, r, &intersectionpoint, &intersectionnormal, &t_intersection); } else if(shape->type == R3_BOX_SHAPE) { R3Box *b = shape->box; intersects = IntersectBox(b, r, &intersectionpoint, &intersectionnormal, &t_intersection); } else if(shape->type == R3_MESH_SHAPE) { R3Mesh *m = shape->mesh; intersects = IntersectMesh(m, r, &intersectionpoint, &intersectionnormal, &t_intersection); } else if(shape->type == R3_CYLINDER_SHAPE) { R3Cylinder *c = shape->cylinder; intersects = IntersectCylinder(c, r, &intersectionpoint, &intersectionnormal, &t_intersection); } else if(shape->type == R3_CONE_SHAPE) { R3Cone *c = shape->cone; intersects = IntersectCone(c, r, &intersectionpoint, &intersectionnormal, &t_intersection); } if(intersects == 1) { if(t_intersection > 0 && t_intersection < *t) { *t = t_intersection; *position = intersectionpoint; *normal = intersectionnormal; *intersectingnode = node; } } } for(unsigned int i=0; i<node->children.size(); i++) { R3Node *child = node->children[i]; R3Node *temp_intersectingnode; if(IntersectBox(&(child->bbox), r, &intersectionpoint, &intersectionnormal, &t_intersection) == 1) { if(t_intersection > *t) continue; } else { continue; } if(IntersectNode(child, r, &intersectionpoint, &intersectionnormal, &t_intersection, &temp_intersectingnode, excludenode) == 1) { if(t_intersection > 0 && t_intersection < *t) { *t = t_intersection; *position = intersectionpoint; *normal = intersectionnormal; *intersectingnode = temp_intersectingnode; } } } if(*t < DBL_MAX) { normal->Transform(tmatrix); normal->Normalize(); position->Transform(tmatrix); if(orig_r.Vector()[0] != 0) *t = (*position - orig_r.Start())[0] / orig_r.Vector()[0]; else if(orig_r.Vector()[1] != 0) *t = (*position - orig_r.Start())[1] / orig_r.Vector()[1]; else *t = (*position - orig_r.Start())[2] / orig_r.Vector()[2]; return 1; } else { return 0; } }
RNBoolean R3Parallel(const R3Ray& ray1, const R3Ray& ray2) { // Return whether ray1 and ray2 are parallel return R3Parallel(ray1.Vector(), ray2.Vector()); }
RNBoolean R3Parallel(const R3Vector& vector, const R3Ray& ray) { // Return whether vector and ray are parallel return R3Parallel(vector, ray.Vector()); }
int IntersectCylinder(R3Cylinder *c, R3Ray r, R3Point *position, R3Vector *normal, double *t) { R3Vector v = r.Vector(); R3Point s = r.Start(); *t = DBL_MAX; // check cylinder sides on infinite double eqa = v[0]*v[0] + v[2]*v[2]; double eqb = 2.0*v[0]*(s[0] - c->Center()[0]) + 2.0*v[2]*(s[2] - c->Center()[2]); double eqc = pow(s[0] - c->Center()[0], 2.0) + pow(s[2] - c->Center()[2], 2.0) - c->Radius()*c->Radius(); double t1, t2; t1 = (-eqb + sqrt(eqb*eqb - 4.0*eqa*eqc))/2.0/eqa; t2 = (-eqb - sqrt(eqb*eqb - 4.0*eqa*eqc))/2.0/eqa; R3Point possible1, possible2; possible1 = r.Start() + r.Vector()*t1; possible2 = r.Start() + r.Vector()*t2; // Check if in y range if(possible1[1] < c->Center()[1] - c->Height()/2.0 || possible1[1] > c->Center()[1] + c->Height()/2.0) { t1 = -1; } if(possible2[1] < c->Center()[1] - c->Height()/2.0 || possible2[1] > c->Center()[1] + c->Height()/2.0) { t2 = -1; } if(t1 > 0 && t2 > 0) { if(t1 > t2) { *t = t2; *position = possible2; *normal = R3Vector(possible2[0] - c->Center()[0], 0, possible2[2] - c->Center()[2]); normal->Normalize(); } else { *t = t1; *position = possible1; *normal = R3Vector(possible1[0] - c->Center()[0], 0, possible1[2] - c->Center()[2]); normal->Normalize(); } } else if(t1 > 0) { *t = t1; *position = possible1; *normal = R3Vector(possible1[0] - c->Center()[0], 0, possible1[2] - c->Center()[2]); normal->Normalize(); } else if(t2 > 0) { *t = t2; *position = possible2; *normal = R3Vector(possible2[0] - c->Center()[0], 0, possible2[2] - c->Center()[2]); normal->Normalize(); } //Check endcaps for(int i=0; i<2; i++) { R3Vector norm_y(0, i == 0 ? 1 : -1, 0); double sign = (i == 0 ? 1.0 : -1.0); R3Point planepoint(c->Center()[0], c->Center()[1] + sign*c->Height()/2.0, c->Center()[2]); R3Plane plane(planepoint, norm_y); R3Point endcap_p; double endcap_t; if(IntersectPlane(&plane, r, &endcap_p, &norm_y, &endcap_t) == 1) { if(pow(endcap_p[0] - c->Center()[0], 2.0) + pow(endcap_p[2] - c->Center()[2], 2.0) < c->Radius()*c->Radius() && endcap_t > 0) { if(endcap_t < *t) { *t = endcap_t; *position = endcap_p; *normal = norm_y; } } } } if(*t < DBL_MAX) { return 1; } else { return 0; } }
RNBoolean R3Parallel(const R3Line& line, const R3Ray& ray) { // Return whether line and ray are parallel return R3Parallel(line.Vector(), ray.Vector()); }
// Compute the radiance emitted back along a given ray from a given intersection R3Rgb ComputeRadiance(R3Scene *scene, R3Ray &ray, R3Intersection &intersection, int depth, int max_depth, int distrib) { R3Rgb il(0, 0, 0, 1); R3Material *mat = intersection.node->material; // add a small epsilon normal to the intersection point so light and secondary rays don't intersect with the same surface R3Point offset_intersection_pos = intersection.position + intersection.normal * EPSILON; // v = a unit vector pointing from the intersection to the ray origin R3Vector v = -ray.Vector(); // if we are not using distributed ray tracing... if (distrib == 0) { // include lighting from all scene lights for (int i = 0; i < scene->NLights(); i++) { R3Light *light = scene->Light(i); // l = a unit vector pointing from the intersection to a light // d = the distance between the intersection and a light R3Vector l; double d; if (light->type == R3_DIRECTIONAL_LIGHT) { l = -light->direction; d = numeric_limits<double>::infinity(); } else { l = (light->position - intersection.position); l.Normalize(); d = (light->position - intersection.position).Length(); } // construct a ray from the intersection toward the light R3Ray light_ray(offset_intersection_pos, l); // compute a shadow intersection and if one exists (that is between the intersection and the light), ignore the light R3Intersection shadow_intersection = ComputeIntersection(scene, scene->Root(), light_ray, d); if (shadow_intersection.hit && shadow_intersection.t < d) continue; // compute the luminance of the light at the intersection position R3Rgb light_il = ComputeLuminance(light, intersection.position); // evaluate the Phong BRDF equations for diffuse and specular reflection double cos_nl = l.Dot(intersection.normal); // r = l reflected across the intersection normal R3Vector r = 2 * cos_nl * intersection.normal - l; double cos_vr = v.Dot(r); // if the light is shining on the front of the surface, include diffuse lighting // if the light is also less than 90 degrees from the camera vector, include specular lighting if (cos_nl > 0) { il += light_il * mat->kd * cos_nl; if (cos_vr > 0) il += light_il * mat->ks * pow(cos_vr, mat->shininess); } } } else // if we ARE using distributed... { if (depth < max_depth) { int nrays = 0; R3Rgb dist_il(0, 0, 0, 1); while (nrays < distrib) { // randomly sample outgoing directions using random variables with normal distributions double u1 = (double)rand()/RAND_MAX; double u2 = (double)rand()/RAND_MAX; double u3 = (double)rand()/RAND_MAX; double u4 = (double)rand()/RAND_MAX; double s1 = sqrt(-2 * log(u1)); double rx = s1 * cos(2 * 3.14159 * u2); double ry = s1 * sin(2 * 3.14159 * u2); double s2 = sqrt(-2 * log(u3)); double rz = s2 * cos(2 * 3.14159 * u4); R3Vector l(rx, ry, rz); l.Normalize(); // check if vector is in the right hemisphere if (l.Dot(intersection.normal) < 0) continue; // generate a random ray and recursively compute the radiance it contributes R3Ray rray(offset_intersection_pos, l); R3Rgb rgb = ComputeRadiance(scene, rray, depth + 1, max_depth, distrib); // evaluate the Phong BRDF equations for diffuse and specular reflection double cos_nl = l.Dot(intersection.normal); // r = l reflected across the intersection normal R3Vector r = 2 * cos_nl * intersection.normal - l; double cos_vr = v.Dot(r); // if the light is shining on the front of the surface, include diffuse lighting // if the light is also less than 90 degrees from the camera vector, include specular lighting if (cos_nl > 0) { dist_il += rgb * mat->kd * cos_nl; if (cos_vr > 0) dist_il += rgb * mat->ks * pow(cos_vr, mat->shininess); } nrays++; } il += dist_il / nrays; } } // if the material has a specular component and the maximum depth hasn't been reached, reflect the camera ray from the // intersection surface and recusively evaluate its radiance if (!mat->ks.IsBlack() && depth < max_depth) { R3Ray spec_ray = GetReflectedRay(intersection, ray); R3Rgb spec = ComputeRadiance(scene, spec_ray, depth + 1, max_depth, distrib); il += spec * mat->ks; } // add refraction component for both distributed and non-distributed techniques if (!mat->kt.IsBlack() && depth < max_depth) { R3Ray trans_ray = GetRefractedRay(intersection, ray, mat->indexofrefraction, scene); R3Rgb trans = ComputeRadiance(scene, trans_ray, depth + 1, max_depth, distrib); il += trans * mat->kt; } // add ambient and emission components il += mat->emission; il += mat->ka * scene->ambient; return il; }
RNBoolean R3Parallel(const R3Ray& ray, const R3Span& span) { // Return whether ray and span are parallel return R3Parallel(ray.Vector(), span.Vector()); }
R3Intersection ComputeIntersection(R3Cylinder *cylinder, R3Ray r) { R3Intersection ret; R3Point p0 = r.Point(0); R3Vector v = r.Vector(); double h = cylinder->Height(); // check the two caps // Bottom First R3Plane bottom = R3negxz_plane; R3Point bottom_center = R3Point(0, -h/2, 0); bottom.Translate(bottom_center - R3null_point); double denom = r.Vector().Dot(bottom.Normal()); double t = (denom == 0) ? -1 : -(R3Vector(p0[0], p0[1], p0[2]).Dot(bottom.Normal()) + bottom.D()) / denom; double dist = R3Distance(r.Point(t), bottom_center); if ((t > EPSILON) && (!ret.hit || t < ret.t) && (dist < cylinder->Radius())) { ret.t = t; ret.normal = bottom.Normal(); ret.position = r.Point(t); ret.hit = true; } // Now Top R3Plane top = R3posxz_plane; R3Point top_center = R3Point(0, cylinder->Height()/2, 0); top.Translate(top_center - R3null_point); denom = r.Vector().Dot(top.Normal()); t = (denom == 0) ? -1 : -(R3Vector(p0[0], p0[1], p0[2]).Dot(top.Normal()) + top.D()) / denom; dist = R3Distance(r.Point(t), top_center); if ((t > EPSILON) && (!ret.hit || t < ret.t) && (dist < cylinder->Radius())) { ret.t = t; ret.normal = top.Normal(); ret.position = r.Point(t); ret.hit = true; } // Now check the side // we know (p0[0] + v[0]*t)^2 + (p0[0] + v[0]*t)^2 = r^2 // so we solve the quadratic double A = v[0]*v[0] + v[2]*v[2]; double B = 2*p0[0]*v[0] + 2*p0[2]*v[2]; double C = p0[0]*p0[0] + p0[2]*p0[2] - cylinder->Radius()*cylinder->Radius(); double det = B*B - 4 * A * C; if (det < 0) return ret; double t_plus = (-B + sqrt(det)) / (2*A); double t_minus = (-B - sqrt(det)) / (2*A); R3Point loc = r.Point(t_minus); if (((t_minus > EPSILON) && (!ret.hit || t_minus < ret.t)) && ((loc[1] > -h/2) && (loc[1] < h/2))) { ret.t = t_minus; ret.position = loc; ret.normal = R3Point(loc[0], 0, loc[2]) - R3null_point; ret.normal.Normalize(); ret.hit = true; } loc = r.Point(t_plus); if (((t_plus > EPSILON) && (!ret.hit || t_plus < ret.t)) && ((loc[1] > -h/2) && (loc[1] < h/2))) { ret.t = t_plus; ret.position = loc; ret.normal = R3Point(loc[0], 0, loc[2]) - R3null_point; ret.normal.Normalize(); ret.hit = true; } return ret; }