boost::optional<Intersection> Triangle::intersect(Ray ray) { Eigen::Vector3f s1 = ray.dir.cross(d2); const float div = s1.dot(d1); if(div <= 0) { // parallel or back return boost::optional<Intersection>(); } const float div_inv = 1 / div; Eigen::Vector3f s = ray.org - p0; const float a = s.dot(s1) * div_inv; if(a < 0 || a > 1) { return boost::optional<Intersection>(); } Eigen::Vector3f s2 = s.cross(d1); const float b = ray.dir.dot(s2) * div_inv; if(b < 0 || a + b > 1) { return boost::optional<Intersection>(); } const float t = d2.dot(s2) * div_inv; if(t < 0) { return boost::optional<Intersection>(); } return boost::optional<Intersection>(Intersection( t, ray.at(t), normal, (1 - a - b) * uv0 + a * uv1 + b * uv2, (1 - a - b) * ir0 + a * ir1 + b * ir2, attribute)); }
bool Triangle::intersect(const Ray &ray, Intersection &intersection) const { const double NdotRayDir = N.dot(ray.direction); if (NdotRayDir < 0.000001 && NdotRayDir > -0.000001) // no hit return false; intersection.t = (D - N.dot(ray.origin)) / (NdotRayDir); const Vector i = ray.at(intersection.t); // intersection point with plane // for an edge p1<->p2, we check to see if the cross product of that edge // vector (p2-p1) and the vector from p1 to the intersection point points // in the same direction as the normal vector if ((p2-p1).cross(i - p1).dot(N) >= 0 && (p3-p2).cross(i - p2).dot(N) >= 0 && (p1-p3).cross(i - p3).dot(N) >= 0) { // i is within the triangle intersection.primitive = const_cast<Triangle*>(this); if (NdotRayDir > 0) intersection.normal = -N; else intersection.normal = N; return true; } return false; }
void fillConicalDiffGeom(Diff_Geom &dg, const Ray &r, float t, bool in) const{ Vector3D pt_inter = r.at(t); Vector3D dNdx, dNdy; Vector3D normal=computenormal(pt_inter); Vector3D dPpdx, dPpdy; r.propagateDifferentials(t, normal, dPpdx, dPpdy); computenormalDifferential(pt_inter, dPpdx, dPpdy, dNdx, dNdy); Vector3D dTdx, dTdy; float u, v; computeConicalTexInfo(pt_inter, normal, dPpdx, dPpdy, u, v, dTdx, dTdy); dg = Diff_Geom(pt_inter, normal, dNdx, dNdy, t, dPpdx, dPpdy, r.dDdx(), r.dDdy(), u, v, dTdx, dTdy, in); }
void EntitiesEditor::computeCursor(int2 start, int2 end, bool floor_mode) { float2 height_off = worldToScreen(int3(0, 0, 0)); start += m_view.pos(); end += m_view.pos(); Ray ray = screenRay(start); Flags::Type flags = Flags::all; if(floor_mode) flags = flags & ~(Flags::wall_tile | Flags::object_tile); auto isect = m_tile_map.trace(ray, -1, flags | Flags::visible); float3 pos = isect.first == -1? (float3)asXZ(screenToWorld(start)) : ray.at(isect.second); m_cursor_pos = (float3)round(pos); m_selection = IRect(min(start, end), max(start, end)); }
Color Scene::trace(const Ray &ray) { // Find hit object and distance Hit min_hit(std::numeric_limits<double>::infinity(),Vector()); Object *obj = NULL; for (unsigned int i = 0; i < objects.size(); ++i) { Hit hit(objects[i]->intersect(ray)); if (hit.t<min_hit.t) { min_hit = hit; obj = objects[i]; } } // No hit? Return background color. if (!obj) return Color(0.0, 0.0, 0.0); Material *material = obj->material; //the hit objects material Point hit = ray.at(min_hit.t); //the hit point Vector N = min_hit.N; //the normal at hit point Vector V = -ray.D; //the view vector /**************************************************** * This is where you should insert the color * calculation (Phong model). * * Given: material, hit, N, V, lights[] * Sought: color * * Hints: (see triple.h) * Triple.dot(Vector) dot product * Vector+Vector vector sum * Vector-Vector vector difference * Point-Point yields vector * Vector.normalize() normalizes vector, returns length * double*Color scales each color component (r,g,b) * Color*Color dito * pow(a,b) a to the power of b ****************************************************/ Color color = material->color; // place holder return color; }
bool Mesh::faceIntersection( const Ray& ray, HitRecord* hitRecord, const Mesh::Face& face) { // Get a point on the plane Vector3D norm; double t; { const auto& p0 = m_verts[face[0]]; const auto& p1 = m_verts[face[1]]; const auto& p2 = m_verts[face[face.size()-1]]; norm = (p1 - p0).cross(p2 - p0); auto rayNorm = ray.dir.dot(norm); // Parallel if (isZero(rayNorm)) return false; t = (p0 - ray.start).dot(norm) / rayNorm; // No intersection if (t < 0 || isZero(t)) { return false; } } // Intersection point auto planePt = ray.at(t); // Now check if planePt is "left" of everything for (size_t i = 0; i < face.size(); ++i) { // Go over points in order const auto& p1 = m_verts[face[i]]; const auto& p2 = m_verts[face[(i + 1) % face.size()]]; // from p1 to p2 const auto side = p2 - p1; // cross from p1 to plane pt and dot against normal auto k = norm.dot(side.cross(planePt - p1)); if (!isZero(k) && k < 0) { // Zero means on the side; negative means opposite dir from norm return false; } } // Update if this is a better t value return hitRecord->update(norm, planePt, t); }
bool Mesh::intersects(const Ray& ray, HitRecord* hitRecord, const Matrix4x4& inverseTransform) { // New ray const auto a = inverseTransform * ray.start; const auto b = inverseTransform * ray.other; const Ray newRay(a, b); HitRecord boundingRecord; HitRecord* boundingPtr; if (BOUNDING_BOX_RENDER) { boundingPtr = hitRecord; } else { boundingPtr = &boundingRecord; } // First, check for intersection against our bounding box if (!boundingCube.intersects(ray, boundingPtr, boundingCubeInverse * inverseTransform)) { return false; } else if (BOUNDING_BOX_RENDER) { return true; } // Note: This is not implemented correctly, since faceIntersection // checks if the face was intersected *and* was a better t value bool hit = false; // For each polygon, check for polygon intersection lol... for (const auto& face : m_faces) { if (faceIntersection(newRay, hitRecord, face)) { // haha, use real ray hitRecord->point = ray.at(hitRecord->t); hitRecord->norm = inverseTransform.transpose() * hitRecord->norm; hitRecord->norm.normalize(); hit = true; } } return hit; }
bool Cylinder::intersects(const Ray& ray, HitRecord* hitRecord, const Matrix4x4& inverseTransform) const { // New ray const auto p1 = inverseTransform * ray.start; const auto p2 = inverseTransform * ray.other; const auto dir = p2 - p1; double t = solveIntersection(p1, dir); if (t < 0) return false; auto localPoint = p1 + t * dir; Vector3D localNorm(0, 0, 0); if (isZero(localPoint[2] + 1)) { localNorm = Vector3D(0, 0, -1); } else if (isZero(localPoint[2] - 1)) { localNorm = Vector3D(0, 0, 1); } else { // Not on one of the tops: Just like a circle localNorm = Vector3D(localPoint[0], localPoint[1], 0); } // t is unchanged by this // Remember to use the *original* intersection point auto point = ray.at(t); // To get the appropriate normal vector, we must also transpose // the inverse transform auto norm = inverseTransform.transpose() * localNorm; norm.normalize(); // We will return whether or not this intersection was // better than whatever was already stored there return hitRecord->update(norm, point, t); }
bool AABB::intersectsRay( const Ray& ray ) const { // if the start point OR the end point is in the AABB, then it's a hit. // For the BOOLEAN check where you don't want the hit point, we use an OR (vs an AND) bool rayStartInside = containsPoint( ray.start ) ; if( rayStartInside || containsPoint( ray.end ) ) return true ; // I don't have to get you the exact point. Vector3f t ; if( rayStartInside ) for( int i = 0 ; i < 3 ; i++ ) if( ray.dir.elts[i] > 0 ) t.elts[i] = ( max.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; else t.elts[i] = ( min.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; else for( int i = 0 ; i < 3 ; i++ ) if( ray.dir.elts[i] > 0 ) t.elts[i] = ( min.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; else t.elts[i] = ( max.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; int tIndex ; if( rayStartInside ) tIndex = t.minIndex() ; else tIndex = t.maxIndex() ; if( isBetweenOrdered( t.elts[tIndex], 0, ray.len ) ) { Vector3f pt = ray.at( t.elts[tIndex] ) ; int o1 = OTHERAXIS1(tIndex) ; int o2 = OTHERAXIS2(tIndex) ; return isBetweenOrdered( pt.elts[o1], min.elts[o1], max.elts[o1] ) && isBetweenOrdered( pt.elts[o2], min.elts[o2], max.elts[o2] ) ; } return false ; }
bool Triangle::doesIntersect (const Ray &ray) const { const double NdotRayDir = N.dot(ray.direction); if (NdotRayDir < 0.000001 && NdotRayDir > -0.000001) // no hit return false; const double t = (D - N.dot(ray.origin)) / (NdotRayDir); const Vector i = ray.at(t); // intersection point with plane if (t < 0) return false; if ((p2-p1).cross(i - p1).dot(N) >= 0 && (p3-p2).cross(i - p2).dot(N) >= 0 && (p1-p3).cross(i - p3).dot(N) >= 0) { // i is within the triangle return true; } return false; }
bool Torus::intersects(const Ray& ray, HitRecord* hitRecord, const Matrix4x4& inverseTransform) const { // New ray const auto p1 = inverseTransform * ray.start; const auto p2 = inverseTransform * ray.other; Ray localRay(p1, p2); double t = solveIntersection(localRay); if (!isValid(t)) return false; Point3D localPoint = localRay.at(t); Point3D globalPoint = ray.at(t); // Get the norm. See: http://www.emeyex.com/site/projects/raytorus.pdf double k = Vector3D(localPoint).length2() - tubeRadius*tubeRadius - 1; Vector3D localNorm( 4 * localPoint[0] * k, 4 * localPoint[1] * k, 4 * localPoint[2] * k + 8 * localPoint[2] ); auto norm = inverseTransform.transpose() * localNorm; norm.normalize(); return hitRecord->update(norm, globalPoint, t); }
std::set<const Model*> UniformGrid::getModels(const Ray& ray) const { std::set<const Model*> models; Point3D nextT(0, 0, 0); // The point *within* the grid where the ray first intersected it Point3D rayStartPoint(0, 0, 0); if (!inGrid(ray.start)) { const auto& sp = startPoint; // Not in the grid: We will use a cube the sz of whole grid to find // the point of entry into the grid auto gridCubeInverse = (translationMatrix(sp[0], sp[0], sp[0]) * gridSizeScaleMatrix).invert(); HitRecord hr; if (!utilityCube.intersects(ray, &hr, gridCubeInverse)) { // Does not intersect the grid even return models; } nextT[0] = hr.t; nextT[1] = hr.t; nextT[2] = hr.t; rayStartPoint = ray.at(hr.t); } else { rayStartPoint = ray.start; } // Place in the grid we are currently stepping through CellCoord gridCoord = coordAt(rayStartPoint); Vector3D dir( std::abs(ray.dir[0]), std::abs(ray.dir[1]), std::abs(ray.dir[2])); // These values are in units of t: how far we must go to travel a whole cell Vector3D dt( isZero(dir[0]) ? 0 : cellSize / dir[0], isZero(dir[1]) ? 0 : cellSize / dir[1], isZero(dir[2]) ? 0 : cellSize / dir[2] ); { // The bottom left corner of the cell we are starting in Point3D gsp = pointAt(gridCoord); // "Grid start point" // Determine how far, in units of t, we have to go in any direction // to reach the next cell // If we are going "forwards" in a coordinate then we need to travel to // gsp + cellSize. If we are going "backwards" in a coordinate then we need // to travel to only gsp. for (int i = 0; i < 3; ++i) { if (isZero(dir[i])) { nextT[i] = -1; continue; } if (ray.dir[i] < 0) { nextT[i] += (rayStartPoint[i] - gsp[i]) / dir[i]; } else { nextT[i] += (gsp[i] + cellSize - rayStartPoint[i]) / dir[i]; } } } // Which direction in the grid to move when we hit a "next" value CellCoord incs( (ray.dir[0] > 0) ? 1 : -1, (ray.dir[1] > 0) ? 1 : -1, (ray.dir[2] > 0) ? 1 : -1 ); // Check if a coord is still valid auto coordOk = [&] (int coord) -> bool { return 0 <= coord && coord < sideLength; }; auto smaller = [] (double a, double b) -> bool { return (b < 0) || a <= b; }; while (coordOk(gridCoord.x) && coordOk(gridCoord.y) && coordOk(gridCoord.z)) { for (const Model* model : cells[indexFor(gridCoord)].models) { models.insert(model); } for (int i = 0; i < 3; ++i) { if (nextT[i] < 0) continue; const auto a = nextT[(i + 1) % 3]; const auto b = nextT[(i + 2) % 3]; if (smaller(nextT[i], a) && smaller(nextT[i], b)) { nextT[i] += dt[i]; gridCoord[i] += incs[i]; break; } } } return models; }
bool AABB::intersects( const Ray& ray, Vector& pt ) { // Rays starting and ending in an octree node must be considered // to hit the node. // if the start point OR the end point is in the AABB, then it's a hit. if( containsIn( ray.startPos ) || containsIn( ray.getEndPoint() ) ) { //generateDebugLines( Vector(0,1,0) ) ;//green is RAY STARTED INSIDE return true ; } Vector tmins, tmaxes ; // These are red in the profiler. tmins = (min - ray.startPos) / ray.direction ; tmaxes = (max - ray.startPos) / ray.direction ; Vector* sols[2] = { &tmins, &tmaxes } ; int solS=0, solI=0 ; real smallestT = HUGE ; // smallest valid t ("so far"). put at inf so first time everything smaller than it. Vector closestRaypoint ; // smallest rayPoint (associated with "smallest t so far") for( int s = 0 ; s < 2 ; s++ ) { Vector& candSols = *sols[s] ; // i walks xyz for( int i = 0 ; i < 3 ; i++ ) { real& t = candSols.e[i] ; // candidate t // validity: 0 <= t <= length if( BetweenIn( t, 0, ray.length ) ) { Vector raypoint = ray.at( t ) ; // AND ON FACE // o1 and o2 are the indices of the OTHER faces, // if i==0, o1=1, o2=2 int o1 = ( i + 1 ) % 3 ; // if( i==0 ) o1=1, o2=2; int o2 = ( i + 2 ) % 3 ; // else if( i == 1) o1=2, o2=0; if( InRect( raypoint.e[ o1 ],raypoint.e[ o2 ], min.e[o1], min.e[o2], max.e[o1], max.e[o2] ) ) { // this one wins, if it's smallest so far if( t < smallestT ) { smallestT = t ; closestRaypoint = raypoint ; // record what face it was solS = s ; solI = i ; } } } } } // now here, it's possible smallestT was unassigned, // which means it'll still be HUGE if( smallestT == HUGE ) { // no solution ///generateDebugLines( Vector(.2,.2,.2) ) ;//total miss return false ; } else { // there was an intersection. ///generateDebugLines( Vector(1,0,0) ) ;//red means it was a hit pt = closestRaypoint ; return true ; } }
bool AABB::intersects( const Ray& ray ) { // VERY IMPORTANT CHECK: If the ray starts inside // the box, then it's a hit. This was important for octrees. if( containsIn( ray.startPos ) || containsIn( ray.getEndPoint() ) ) return true ; #define TECH 0 #if TECH==0 // the algorithm says, find 3 t's, Vector t ; // LARGEST t is the only only we need to test if it's on the face. for( int i = 0 ; i < 3 ; i++ ) { if( ray.direction.e[i] > 0 ) // CULL BACK FACE t.e[i] = ( min.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ; else t.e[i] = ( max.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ; } int mi = t.maxIndex() ; if( BetweenIn( t.e[mi], 0, ray.length ) ) { Vector pt = ray.at( t.e[mi] ) ; // check it's in the box in other 2 dimensions int o1 = ( mi + 1 ) % 3 ; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc. int o2 = ( mi + 2 ) % 3 ; return BetweenIn( pt.e[o1], min.e[o1], max.e[o1] ) && BetweenIn( pt.e[o2], min.e[o2], max.e[o2] ) ; } return false ; #elif TECH==1 // This culls the back faces first, and gives you the answer with the largest valid t for( int i = 0 ; i < 3 ; i++ ) { int o1 = ( i + 1 ) % 3 ; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc. int o2 = ( i + 2 ) % 3 ; // min.x (NX) is possible, PX won't be hit. (unless you're inside the box.) if( ray.direction.e[i] > 0 ) // CULL BACK FACE { real t = ( min.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ; // 1. CHECK VALID T if( BetweenIn( t, 0, ray.length ) ) { // 2. CHECK IN BOX Vector pt = ray.at( t ) ; if( BetweenIn( pt.e[o1], min.e[o1], max.e[o1] ) && BetweenIn( pt.e[o2], min.e[o2], max.e[o2] ) ) return true ; // it's valid in the box } } else if( ray.direction.e[i] < 0 ) // max.x (PX) is possible { real t = ( max.e[i] - ray.startPos.e[i] ) / ray.direction.e[i] ; if( BetweenIn( t, 0, ray.length ) ) // valid t { Vector pt = ray.at( t ) ; if( BetweenIn( pt.e[o1], min.e[o1], max.e[o1] ) && BetweenIn( pt.e[o2], min.e[o2], max.e[o2] ) ) return true ; // it's valid in the box } } } return false ; // no hit. #elif TECH==2 // This solves ALL 6 solutions and finds the shortest t. // Rays starting and ending in an octree node must be considered // to hit the node. // if the start point OR the end point is in the AABB, then it's a hit. // These are red in the profiler. Vector tmins = (min - ray.startPos) / ray.direction ; Vector tmaxes = (max - ray.startPos) / ray.direction ; Vector* sols[2] = { &tmins, &tmaxes } ; for( int s = 0 ; s < 2 ; s++ ) { Vector& candSols = *sols[s] ; // i walks xyz for( int i = 0 ; i < 3 ; i++ ) { real& t = candSols.e[i] ; // candidate t // validity: 0 <= t <= length if( BetweenIn( t, 0, ray.length ) ) { Vector raypoint = ray.at( t ) ; // AND ON FACE // o1 and o2 are the indices of the OTHER faces, // if i==0, o1=1, o2=2 int o1 = ( i + 1 ) % 3 ; // if( i==0 ) o1=1, o2=2; int o2 = ( i + 2 ) % 3 ; // else if( i == 1) o1=2, o2=0; if( InRect( raypoint.e[ o1 ],raypoint.e[ o2 ], min.e[o1], min.e[o2], max.e[o1], max.e[o2] ) ) { // the box got hit, any side return true ; } } } } // No hit return false ; #endif }
// Moeller-Trumbore bool Triangle::hit(const Ray& ray, const Ray_Tracer* rt, float t_min, float t_max, Ray_Hit& rh, bool shadow) const { // Absolute Triangle Vertex Positions V3& v0 = m->verts[inds[0]]; V3& v1 = m->verts[inds[1]]; V3& v2 = m->verts[inds[2]]; // Two edges of triangle V3 e0, e1; e0 = v1 - v0; e1 = v2 - v0; V3 p = ray.s.cross(e1); float deter = e0.dot(p); if (deter > -EPSILON && deter < EPSILON) return false; V3 t = ray.o - v0; // Store the inverse to reduce divisions float inv_deter = 1.0 / deter; float u = t.dot(p) * inv_deter; if (u < 0.0 || u > 1.0) return false; V3 q = t.cross(e0); float v = ray.s.dot(q) * inv_deter; if (v < 0.0 || u + v > 1.0) return false; float t_inter = e1.dot(q) * inv_deter; if (t_inter < t_min || t_inter > t_max) { //std::cout << "Cull: " << t_inter << '\t' <<t_min<< '\t' <<t_max<< std::endl; return false; } rh.t = t_inter; rh.col = 0.2*m->mat.col; rh.shape = m; if (shadow || ray.depth >= rt->depth_limit) return true; /* if (is_light) { rh.col = Color(1.0, 1.0, 1.0); return true; } */ V3 int_loc = ray.at(t_inter); // Shadow for (Shape* light : rt->lights) { // BIG ASSUMPTION THAT ALL LIGHTS ARE SPHERES Sphere* sph = static_cast<Sphere*>(light); // Make ray from intersection to light V3 int_to_light = sph->c - int_loc; float dist_to_light = int_to_light.norm(); int_to_light.normalize(); Ray shadow_ray(int_loc + EPSILON*int_to_light, int_to_light, ray.depth + 1); Ray_Hit shadow_hit; if (rt->kd->hit_helper(true, rt->kd->root, shadow_ray, rt, EPSILON, dist_to_light, shadow_hit, true, 0)) { if (!shadow_hit.shape->is_light) { continue; } } float inner = int_to_light.dot(normal); if (inner > 0.0) { rh.col += sph->mat.col *inner* m->mat.diff * m->mat.col; } } // Reflection V3 refl_dir = ray.s - 2.0f * (normal.dot(ray.s)) * normal; Ray refl_ray(int_loc + EPSILON*refl_dir, refl_dir, ray.depth + 1); Ray_Hit refl_hit; if (rt->kd->hit_helper(true, rt->kd->root, refl_ray, rt, EPSILON, FLT_MAX, refl_hit, false, 0)) { //if (rt->trace(refl_ray, EPSILON, FLT_MAX, refl_hit, false)) { rh.col += m->mat.refl*refl_hit.col * refl_hit.shape->mat.col; } return true; }
bool AABB::intersect(const Ray& ray, float& t, glm::vec3& normal, uint64_t moxelIndex) { float normalDirection[3] = {1.0f,1.0f,1.0f}; normal = glm::vec3(0.0f,0.0f,0.0f); //cout << "AABB: "; //print(); //cout << "Ray: "; //ray.print(); // If the ray starts inside the box, or ends inside if (contains(ray.position)) { return false; } // the algorithm says, find 3 t's, glm::vec3 tVals; // LARGEST t is the only one we need to test if it's on the face. for (int i = 0 ; i < 3 ; i++) { if (ray.direction[i] == 0) { tVals[i] = -FLT_MAX; } else if(ray.direction[i] > 0) // CULL BACK FACE { tVals[i] = ( mins[i] - ray.position[i] ) / ray.direction[i]; // If the ray direction is positive, it will hit the more negative AABB side which makes // the normal point in the negative direction normalDirection[i] = -1.0f; } else { tVals[i] = ( maxs[i] - ray.position[i] ) / ray.direction[i]; // If the ray direction is positive, it will hit the more positive AABB side which makes // the normal point in the positive direction normalDirection[i] = 1.0f; } } // cout << "tVals: "; // for (int i = 0; i < 3; ++i) // { // cout << "(" << i << ": " << tVals[i] << ") "; // } // cout << endl; // Get the max index float maxValue = tVals[0]; int maxIndex = 0; if (tVals[1] > maxValue) { maxValue = tVals[1]; maxIndex = 1; } if (tVals[2] > maxValue) { maxValue = tVals[2]; maxIndex = 2; } t = maxValue; normal[maxIndex] = normalDirection[maxIndex]; if (tVals[maxIndex] > 0.0f) { glm::vec3 pt = ray.at(tVals[maxIndex]); // check it's in the box in other 2 dimensions int o1 = ( maxIndex + 1 ) % 3; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc. int o2 = ( maxIndex + 2 ) % 3; return inRange(pt[o1], mins[o1], maxs[o1]) && inRange(pt[o2], mins[o2], maxs[o2]) ; } return false ; // the ray did not hit the box. }
void Scene::jiggle(Ray& ray){ Point pjig = ray.at(pow(2,-32)); ray = Ray(pjig,ray.D); }
pair<int, float> Grid::trace(const Ray &ray, float tmin, float tmax, int ignored_id, int flags) const { float3 p1 = ray.at(tmin), p2 = ray.at(tmax); int2 pos = worldToGrid((int2)p1.xz()), end = worldToGrid((int2)p2.xz()); //TODO: verify for rays going out of grid space if(!isInsideGrid(pos) || !isInsideGrid(end)) return make_pair(-1, constant::inf); // Algorithm idea from: RTCD by Christer Ericson int dx = end.x > pos.x? 1 : end.x < pos.x? -1 : 0; int dz = end.y > pos.y? 1 : end.y < pos.y? -1 : 0; float cell_size = (float)node_size; float inv_cell_size = 1.0f / cell_size; float lenx = fabs(p2.x - p1.x); float lenz = fabs(p2.z - p1.z); float minx = float(node_size) * floorf(p1.x * inv_cell_size), maxx = minx + cell_size; float minz = float(node_size) * floorf(p1.z * inv_cell_size), maxz = minz + cell_size; float tx = (p1.x > p2.x? p1.x - minx : maxx - p1.x) / lenx; float tz = (p1.z > p2.z? p1.z - minz : maxz - p1.z) / lenz; float deltax = cell_size / lenx; float deltaz = cell_size / lenz; int out = -1; float out_dist = tmax + constant::epsilon; while(true) { int node_id = nodeAt(pos); const Node &node = m_nodes[node_id]; if(flagTest(node.obj_flags, flags) && intersection(ray, node.bbox) < out_dist) { const Object *objects[node.size]; int count = extractObjects(node_id, objects, ignored_id, flags); for(int n = 0; n < count; n++) { float dist = intersection(ray, objects[n]->bbox); if(dist < out_dist) { out_dist = dist; out = objects[n] - &m_objects[0]; } } if(node.is_dirty) updateNode(node_id); } if(tx <= tz || dz == 0) { if(pos.x == end.x) break; tx += deltax; pos.x += dx; } else { if(pos.y == end.y) break; tz += deltaz; pos.y += dz; } float ray_pos = tmin + max((tx - deltax) * lenx, (tz - deltaz) * lenz); if(ray_pos >= out_dist) break; } return make_pair(out, out_dist); }
Color Scene::trace(const Ray &ray, int steps,double eta1) { // Find hit object and distance Hit min_hit(std::numeric_limits<double>::infinity(),Vector()); Object *obj = NULL; for (unsigned int i = 0; i < objects.size(); ++i) { Hit hit(objects[i]->intersect(ray)); if (hit.t<min_hit.t) { min_hit = hit; obj = objects[i]; } } // No hit? Return background color. if (!obj) return Color(0.0, 0.0, 0.0); Material *material = obj->material; //the hit objects material Point hit = ray.at(min_hit.t); //the hit point Vector N = min_hit.N; //the normal at hit point Vector V = -ray.D; //the view vector /**************************************************** * This is where you should insert the color * calculation (Phong model). * * Given: material, hit, N, V, lights[] * Sought: color * * Hints: (see triple.h) * Triple.dot(Vector) dot product * Vector+Vector vector sum * Vector-Vector vector difference * Point-Point yields vector * Vector.normalize() normalizes vector, returns length * double*Color scales each color component (r,g,b) * Color*Color dito * pow(a,b) a to the power of b ****************************************************/ // extract and/or declare variables // for lighting calculations Color ambient = Color(1.0,1.0,1.0); // ambient colour Color base = material->color; // material colour double ka = material->ka; // ambient intensity; double kd = material->kd; // diffuse intensity double ks = material->ks; // specular intensity double e = material->n; // exponent of specular highlight size double reflect = material->reflect; // reflect coefficient double refract = material->refract; // refraction coefficient double eta2 = material->eta; // refraction index if(eta1 == eta2){ eta2 = AIR_ETA; } // get reflected ray Vector vec_ref = ray.D - (2.0 *(ray.D.dot(N))*N); // reflect ray direction Ray ray_ref(hit,vec_ref); //reflect ray // jiggle the ray jiggle(ray_ref); // hack Vector frac_n; if(ray.D.dot(N) < 0.0){ frac_n = N; }else{ frac_n = -N; } // get refracted ray bool frac_flag; Vector frac_dir = fractf(eta1,eta2,ray.D,frac_n); // direction of refraction Ray ray_frac(hit,frac_dir); // ray going out of the material if(frac_dir.length_2() > 0.0 && refract > 0.0){ frac_flag = true; }else{ frac_flag = false; } // jiggle the ray jiggle(ray_frac); Color c_ref; // colour of reflected ray Color c_frac; // colour of refracted ray // recursively trace reflected/refracted rays up to steps times if(steps > 0){ if(reflect > 0.0) c_ref = trace(ray_ref,steps-1,eta1); if(frac_flag) c_frac = trace(ray_frac, steps-1,eta2); } Color color = ka * base * ambient; // set ambient colour for(unsigned int i = 0;i<lights.size();i++){ bool shaded = false; // flag if the current light cast a shadow Vector L = hit - lights[i]->position; // vector of light direction Vector SL = lights[i]->position - hit; // vector of shadow feeler L.normalize(); SL.normalize(); // get shadow feelers Ray feeler = Ray(hit,SL); //jiggle Ray jiggle(feeler); // test to see if object is in shadow if(shadows){ for(unsigned int j = 0;j<objects.size();j++){ Hit test(objects[j]->intersect(feeler)); if(test.t >= 0.0) { shaded = true; break; } } } if(!shaded){ Color lc = lights[i]->color; // colour of light double lnDot = L.dot(N); // dot product of light Vector R = L - (2.0*N*lnDot); // reflection vector R.normalize(); double rvDot = R.dot(V) ; color += (kd*base*lc*max(0.0,lnDot)) + (ks*lc*pow(max(0.0,rvDot),e)); } } color += reflect*c_ref + refract*c_frac; return color; }
/** * @brief intersect a geometric object. * @param ray_ the ray * @param p_diff_geom_ the result of the intersection * @return true if the ray intersects the geometry. Modifies the ray_.max_t() value, intersect only if the intersection * is before ray_.max_t() * @todo Factorize code shared by Box3D::clip() * * Adapted from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrLine3Box3.cpp */ bool intersect (Ray& ray_, Diff_Geom * p_diff_geom_) const { float t0 = -ray_.ray_epsilon(); float t1 = ray_.max_t(); // Convert linear component to box coordinates. Vector3D diff = ray_.ori() - m_center; Vector3D BOrigin( diff.dot(m_axis[0]), diff.dot(m_axis[1]), diff.dot(m_axis[2]) ); Vector3D BDirection( ray_.dir().dot(m_axis[0]), ray_.dir().dot(m_axis[1]), ray_.dir().dot(m_axis[2]) ); float saveT0 = t0, saveT1 = t1; bool notAllClipped; unsigned char updated; int nt0, nt1; notAllClipped = clip(+BDirection.x(), -BOrigin.x()-m_extent[0], t0, t1, updated); if (updated == 1) nt0 = 0; else if (updated == 2) nt1 = 0; notAllClipped = notAllClipped && clip(-BDirection.x(), +BOrigin.x()-m_extent[0], t0, t1, updated); if (updated == 1) nt0 = 1; else if (updated == 2) nt1 = 1; notAllClipped = notAllClipped && clip(+BDirection.y(), -BOrigin.y()-m_extent[1], t0, t1, updated); if (updated == 1) nt0 = 2; else if (updated == 2) nt1 = 2; notAllClipped = notAllClipped && clip(-BDirection.y(), +BOrigin.y()-m_extent[1], t0, t1, updated); if (updated == 1) nt0 = 3; else if (updated == 2) nt1 = 3; notAllClipped = notAllClipped && clip(+BDirection.z(), -BOrigin.z()-m_extent[2], t0, t1, updated); if (updated == 1) nt0 = 4; else if (updated == 2) nt1 = 4; notAllClipped = notAllClipped && clip(-BDirection.z(), +BOrigin.z()-m_extent[2], t0, t1, updated); if (updated == 1) nt0 = 5; else if (updated == 2) nt1 = 5; if (notAllClipped && (t0 != saveT0 || t1 != saveT1)) { if ( (t0 > ray_.ray_epsilon() ) && (t0 < ray_.max_t()) ){ Vector3D normal = m_axis[nt0/2]; if (nt0%2) normal = -normal; p_diff_geom_->set_pos(ray_.at(t0)); p_diff_geom_->set_normal(normal); p_diff_geom_->set_t(t0); ray_.set_max_t(t0); return true; } else if ( (t1 > ray_.ray_epsilon()) && (t1 < ray_.max_t()) ){ Vector3D normal = m_axis[nt1/2]; if (nt1%2) normal = -normal; p_diff_geom_->set_pos(ray_.at(t1)); p_diff_geom_->set_normal(normal); p_diff_geom_->set_t(t1); ray_.set_max_t(t1); return true; } else return false; } else { return false; } }
int get_clip_points(Ray& ray_, Diff_Geom ipoints[2], float t[2]) const{ int numintersection = 0; Vector3D E = ray_.ori() - m_vertex; float AdD = m_axis.dot(ray_.dir()); float cosSqr = m_costheta*m_costheta; float AdE = m_axis.dot(E); float DdE = ray_.dir().dot(E); float EdE = E.dot(E); float c2 = AdD*AdD-cosSqr; float c1 = AdD*AdE - cosSqr*DdE; float c0 = AdE*AdE - cosSqr*EdE; float dot; Vector3D zero; if (std::fabs(c2)>0) { float discr = c1*c1 - c0*c2; float invC2 = 1.f/c2; if (discr < 0) { // No intersection return 0; } else if (discr > 0) { // two distinct intersections float root = std::sqrt(discr); // entry point t[numintersection] = (-c1 + root) * invC2; E = ray_.at(t[numintersection]) - m_vertex; dot = E.dot(m_axis); if ( (dot > 0) && (dot <= m_height)) { fillConicalDiffGeom(ipoints[numintersection], ray_, t[numintersection], true); ++numintersection; } // exit point t[numintersection] = (-c1 - root) * invC2; E = ray_.at(t[numintersection]) - m_vertex; dot = E.dot(m_axis); if ( (dot > 0) && (dot <= m_height)) { fillConicalDiffGeom(ipoints[numintersection], ray_, t[numintersection], false); ++numintersection; } return numintersection; } else { // one reapeated intersection : ray is tangent to the cone // may be return 0 instead of an intersection ? return 0; t[numintersection] = -c1 * invC2; E = ray_.at(t[numintersection]) - m_vertex; dot = E.dot(m_axis); if ( (dot > 0) && (dot <= m_height)) { fillConicalDiffGeom(ipoints[numintersection], ray_, t[numintersection], true); ++numintersection; } return numintersection; } } else if (std::fabs(c1) > 0) { // the ray is on the boundary of the cone // we consider no intersection // TODO : check this for CSG return 0; } else { //return false; // Cone contains ray V+tD // The ray intersect the cone exactly at its vertex :( // TODO : manage this particular case in another function ipoints[numintersection].set_pos(m_vertex); ipoints[numintersection].set_normal(-m_axis); ipoints[numintersection].set_u(1.f); ipoints[numintersection].set_v(0.f); E = ray_.ori() - m_vertex; t[numintersection] = ( (E.dot(ray_.dir())<0) ? std::sqrt(EdE) : -std::sqrt(EdE) ) ; ipoints[numintersection].set_t(t[numintersection]); ipoints[numintersection].set_in(true); ipoints[numintersection].set_u(0.f); ipoints[numintersection].set_v(1.f); // todo : compute here normal derivatives (according to x and y directions on the image plane) ipoints[numintersection].set_dNdx(zero); ipoints[numintersection].set_dNdy(zero); ++numintersection; // check with cap plane Plane cap(m_vertex + m_axis * m_height, m_axis); IntervalSet capset; if (cap.clip(ray_, capset)) { if (capset.bounds()[0].t < t[numintersection-1]) { t[numintersection] = t[numintersection-1]; ipoints[numintersection] = ipoints[numintersection-1]; --numintersection; } else { capset.bounds()[0].data->set_in(false); } ipoints[numintersection] = *(capset.bounds()[0].data); // TODO : update u, v dTdx and dTdy ipoints[numintersection].set_u(ipoints[numintersection].u()/(PSCALE*m_radius)); ipoints[numintersection].set_v(ipoints[numintersection].v()/(PSCALE*m_radius)); ipoints[numintersection].set_dTdx(ipoints[numintersection].dTdx()* (1.f/(PSCALE*m_radius))); ipoints[numintersection].set_dTdy(ipoints[numintersection].dTdy()* (1.f/(PSCALE*m_radius))); delete capset.bounds()[0].data; delete capset.bounds()[1].data; return 2; } else { // must never reach this point ! assert(false); return 0; } } }
Hit Box::intersect(const Ray &ray) { /**************************************************** * RT1.1: INTERSECTION CALCULATION * t is the distance between the ray origin and the closest face of the box ****************************************************/ /* Mathematic Method * We consider a cube like the intersection of 3 slabs * We first start to calculate the distance between the ray and the 2D slab on x,y of the cube * Then repeat it for z */ double t, tentry, texit, tyentry, tyexit, tzentry, tzexit; /* A box is defined with two point: the minimum and maximum exent points (min and max) */ /* extents[0] = min extent of the box & extents[1] = max extent of the box*/ Point extents[2] = {min,max}; /* First we start in 2D * Exemple for tentry initialized with txentry: * ray.O.x + txentry*ray.D.x = min.x (= extents[0].x) if ray.D.x >= 0 * <=> tentryx = (min.x - ray.O.x)/ ray.D.x * or * ray.O.x + txentry*ray.D.x = max.x (= extents[1].x) if ray.D.x < 0 * <=> tentryx = (max.x - ray.O.x)/ ray.D.x */ tentry = (extents[(ray.D.x < 0.0 && ray.D.x != -0.0)].x - ray.O.x) / ray.D.x; texit = (extents[1-(ray.D.x < 0.0 && ray.D.x != -0.0)].x - ray.O.x) / ray.D.x; tyentry = (extents[(ray.D.y < 0.0 && ray.D.y != -0.0)].y - ray.O.y) / ray.D.y; tyexit = (extents[1-(ray.D.y < 0.0 && ray.D.y != -0.0)].y - ray.O.y) / ray.D.y; /* Case where there is no Hit*/ if ((tentry > tyexit) || (tyentry > texit)){ return Hit::NO_HIT(); } /* Case where the entry point is on y */ if (tyentry > tentry) { tentry = tyentry; } /* Case where the exit point is on y */ if (tyexit < texit) { texit = tyexit; } /* The we compare tentry and texit with the 3rd dimension */ tzentry = (extents[(ray.D.z < 0.0 && ray.D.z != -0.0)].z - ray.O.z) / ray.D.z; tzexit = (extents[1-(ray.D.z < 0.0 && ray.D.z != -0.0)].z - ray.O.z) / ray.D.z; /* Case where there is no Hit*/ if ((tentry > tzexit) || (tzentry > texit)) return Hit::NO_HIT(); /* Case where the entry point is on z */ if (tzentry > tentry) { tentry = tzentry; } /* Case where the exit point is on z */ if (tzexit < texit) { texit = tzexit; } /* Choose t according to the tentry and texit cases*/ if (tentry <= 0){ t = texit; } else{ t = tentry; } /**************************************************** * RT1.2: NORMAL CALCULATION ****************************************************/ Vector N; Point a,b; Vector v; Point center = Point((min.x + max.x)/2.0,(min.y + max.y)/2.0,(min.z + max.z)/2.0); Point hit = ray.at(t); double side_x = fabs(max.x - min.x)/2.0; double side_y = fabs(max.y - min.y)/2.0; double side_z = fabs(max.z - min.z)/2.0; if (hit.y <= center.y - side_y + 0.1 && hit.y >= center.y - side_y - 0.1) { // Face 6 a = Point(center.x,center.y - side_y,center.z); //cout << "6"; } else if ( hit.y <= center.y + side_y + 0.1 && hit.y >= center.y + side_y - 0.1) { // Face 3 a = Point(center.x,center.y + side_y,center.z); //cout << "3"; } else if (hit.x <= center.x - side_x + 0.1 && hit.x >= center.x - side_x - 0.1) { // Face 5 a = Point(center.x- side_x,center.y,center.z); //cout << "5"; } else if ( hit.x <= center.x + side_x + 0.1 && hit.x >= center.x + side_x - 0.1) { // Face 2 a = Point(center.x+ side_x,center.y ,center.z); //cout << "2"; } else if (hit.z <= center.z - side_z + 0.1 && hit.z >= center.z - side_z - 0.1) { // Face 4 a = Point(center.x,center.y,center.z- side_z); //cout << "4"; } else if (hit.z <= center.z + side_z + 0.1 && hit.z >= center.z + side_z - 0.1) { // Face 1 a = Point(center.x,center.y,center.z+ side_z); //cout << "1"; } else { int x = center.x + side_x; cout << "?- " << hit.x << " c: " << (int)(x) <<"-"; cout << ((int) (hit.x) >= (int)(x)) <<"-"; } v = hit - a; b = center + v; N = (hit - b).normalized(); /* Choose of the good direction if (N.dot(ray.D) > 0){ N = -N; }*/ //cout << "x :" << hit.x <<" y :" << hit.y <<" z :" << hit.z << "\n" ; return Hit(t,N); }
// ----------------------------------------------------- tracePhong ------------------------------------------------------------------ // Color Scene::tracePhongGooch(const Ray &ray, int recursionDepth){ // Find hit object and distance Hit min_hit(std::numeric_limits<double>::infinity(),Vector()); Object *obj = NULL; for (unsigned int i = 0; i < objects.size(); ++i) { Hit hit(objects[i]->intersect(ray)); if (hit.t<min_hit.t) { min_hit = hit; obj = objects[i]; } } // No hit? Return background color. if (!obj) return Color(0.0, 0.0, 0.0); Material *material = obj->material; //the hit objects material Point hit = ray.at(min_hit.t); //the hit point Vector N = min_hit.N; //the normal at hit point Vector V = -ray.D; //the view vector /**************************************************** * This is where you should insert the color * calculation (Phong model). * * Given: material, hit, N, V, lights[] * Sought: color * * Hints: (see triple.h) * Triple.dot(Vector) dot product * Vector+Vector vector sum * Vector-Vector vector difference * Point-Point yields vector * Vector.normalize() normalizes vector, returns length * double*Color scales each color component (r,g,b) * Color*Color dito * pow(a,b) a to the power of b ****************************************************/ Color color; //For each light for(unsigned int i = 0; i < lights.size(); i++){ bool isShadow = false; if (rendermode == phong) //Ambiant color += lights[i]->color * material->color * material->ka; //Shadow if(Shadow){ //Ray from light to object Ray light(lights[i]->position, hit - lights[i]->position); //Hit from light to nearest object Hit minHit = findMinHit(light); //Hit from current object to light Hit currentHit(obj->intersect(light)); // if (minHit.t < currentHit.t) isShadow = true; } //No Shadow -> Calculate colors if(!isShadow){ //The light vector Vector L = lights[i]->position - hit;L.normalize(); //The R vector (required for specular shading) Vector R = (-1*L) + 2 * (L.dot(N)) * N; if (rendermode == phong) //Diffuse if (material->texture){ color += (max(0.0,N.dot(L)) * lights[i]->color) * obj->calcTexture(hit) * material->kd; } else { color += (max(0.0,N.dot(L)) * lights[i]->color) * material->color * material->kd; } if (rendermode == gooch && ray.D.dot(N)<-0.2){ Color kCool = Color(0.0,0.0,kBlue) + alpha * (lights[i]->color * material->color * material->kd); Color kWarm = Color(kYellow,kYellow,0.0) + beta * (lights[i]->color * material->color * material->kd); color += (kCool *(1 - N.dot(L))/2) + (kWarm * (1 + N.dot(L))/2); } //Specular + dot(R,V)^n LightColor ks color += pow(max(0.0,R.dot(V)), material->n) * lights[i]->color * material->ks; } } //Reflection if(recursionDepth <= maxRecursionDepth && ray.D.dot(N)<-0.2){ Color buffer(0.0, 0.0, 0.0); //The reflected direction Vector reflectV((V + 2*(-N.dot(V))*N)); for (unsigned int i=0; i<4; i++){ //The reflected ray Ray reflect(hit + 0.001 * N, -reflectV); //Calculate reflection through recursion buffer += material->ks*tracePhongGooch(reflect, recursionDepth+1); reflectV.x += 0.01 * cos(0.5); reflectV.y += 0.01 * sin(0.5); } //Color = average buffer value color += buffer/4; } return color; }
/** * @brief get_clip_points * @param ray_ * @param ipoints * @param t * @return the number of clipping position on the ray (0 to 2) */ int get_clip_points(Ray& ray_, Diff_Geom ipoints[2], float t[2]) const{ int numintersection = 0; Vector3D E = ray_.ori() - m_vertex; float AdD = m_axis.dot(ray_.dir()); float cosSqr = m_costheta*m_costheta; float AdE = m_axis.dot(E); float DdE = ray_.dir().dot(E); float EdE = E.dot(E); float c2 = AdD*AdD-cosSqr; float c1 = AdD*AdE - cosSqr*DdE; float c0 = AdE*AdE - cosSqr*EdE; float dot; if (std::fabs(c2)>0) { float discr = c1*c1 - c0*c2; float invC2 = 1.f/c2; if (discr < 0) { // No intersection return 0; } else if (discr > 0) { // two distinct intersections float root = std::sqrt(discr); // entry point t[numintersection] = (-c1 + root) * invC2; ipoints[numintersection].set_pos(ray_.at(t[numintersection])); E = ipoints[numintersection].pos() - m_vertex; dot = E.dot(m_axis); if ( (dot > 0) && (dot <= m_height)) { Vector3D normal=compute_normal(ipoints[numintersection].pos()); ipoints[numintersection].set_normal(normal); ipoints[numintersection].set_t(t[numintersection]); ++numintersection; } // exit point t[numintersection] = (-c1 - root) * invC2; ipoints[numintersection].set_pos(ray_.at(t[numintersection])); E = ipoints[numintersection].pos() - m_vertex; dot = E.dot(m_axis); if ( (dot > 0) && (dot <= m_height)) { Vector3D normal=compute_normal(ipoints[numintersection].pos()); ipoints[numintersection].set_normal(normal); ipoints[numintersection].set_t(t[numintersection]); ++numintersection; } return numintersection; } else { // one reapeated intersection : ray is tangent to the cone // may be return 0 instead of an intersection ? return 0; t[numintersection] = -c1 * invC2; ipoints[numintersection].set_pos(ray_.at(t[numintersection])); E = ipoints[numintersection].pos() - m_vertex; dot = E.dot(m_axis); if ( (dot > 0) && (dot <= m_height)) { Vector3D normal=compute_normal(ipoints[numintersection].pos()); ipoints[numintersection].set_normal(normal); ipoints[numintersection].set_t(t[numintersection]); ++numintersection; } return numintersection; } } else if (std::fabs(c1) > 0) { // the ray is on the boundary of the cone // we consider no intersection // TODO : check this for CSG return 0; } else { //return false; // Cone contains ray V+tD // The ray intersect the cone exactly at its vertex :( ipoints[numintersection].set_pos(m_vertex); ipoints[numintersection].set_normal(-m_axis); E = ray_.ori() - m_vertex; t[numintersection] = ( (E.dot(ray_.dir())<0) ? std::sqrt(EdE) : -std::sqrt(EdE) ) ; ipoints[numintersection].set_t(t[numintersection]); ++numintersection; // TODO compute cap plane intersection // check with cap plane Plane cap(m_vertex + m_axis * m_height, m_axis); IntervalSet capset; if (cap.clip(ray_, capset)) { if (capset.bounds()[0].t < t[numintersection-1]) { t[numintersection] = t[numintersection-1]; ipoints[numintersection] = ipoints[numintersection-1]; --numintersection; } ipoints[numintersection] = *(capset.bounds()[0].data); delete capset.bounds()[0].data; delete capset.bounds()[1].data; return 2; } else { // must never reach this point ! assert(false); return 0; } } }
/** * @brief clip a ray by a Box3D. * @param ray_ the ray to clip * @param bounds ray sorted bounds of the resulting clipping segment. * @return true if ray was clipped. bounds parameter is filld by this method * * For simple convex objects, there is two values in bounds that represent in and out events. * An in event is whe the ray enters the geometry, an out is when the ray leaves the geometry. * * @todo Factorize code shared by Box3D::intersect() * * Adapted from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrLine3Box3.cpp * */ bool clip(Ray& ray_, IntervalSet &bounds) const { float t0 = std::numeric_limits<float>::min(); float t1 = std::numeric_limits<float>::max(); // Convert linear component to box coordinates. Vector3D diff = ray_.ori() - m_center; Vector3D BOrigin( diff.dot(m_axis[0]), diff.dot(m_axis[1]), diff.dot(m_axis[2]) ); Vector3D BDirection( ray_.dir().dot(m_axis[0]), ray_.dir().dot(m_axis[1]), ray_.dir().dot(m_axis[2]) ); float saveT0 = t0, saveT1 = t1; bool notAllClipped; unsigned char updated; int nt0, nt1; notAllClipped = clip(+BDirection.x(), -BOrigin.x()-m_extent[0], t0, t1, updated); if (updated == 1) nt0 = 0; else if (updated == 2) nt1 = 0; notAllClipped = notAllClipped && clip(-BDirection.x(), +BOrigin.x()-m_extent[0], t0, t1, updated); if (updated == 1) nt0 = 1; else if (updated == 2) nt1 = 1; notAllClipped = notAllClipped && clip(+BDirection.y(), -BOrigin.y()-m_extent[1], t0, t1, updated); if (updated == 1) nt0 = 2; else if (updated == 2) nt1 = 2; notAllClipped = notAllClipped && clip(-BDirection.y(), +BOrigin.y()-m_extent[1], t0, t1, updated); if (updated == 1) nt0 = 3; else if (updated == 2) nt1 = 3; notAllClipped = notAllClipped && clip(+BDirection.z(), -BOrigin.z()-m_extent[2], t0, t1, updated); if (updated == 1) nt0 = 4; else if (updated == 2) nt1 = 4; notAllClipped = notAllClipped && clip(-BDirection.z(), +BOrigin.z()-m_extent[2], t0, t1, updated); if (updated == 1) nt0 = 5; else if (updated == 2) nt1 = 5; if (notAllClipped && (t0 != saveT0 || t1 != saveT1)) { if (t1 > t0) { Vector3D normal = m_axis[nt0/2]; if (nt0%2) normal = -normal; Diff_Geom *in = new Diff_Geom(ray_.at(t0), normal, t0); normal = m_axis[nt1/2]; if (nt1%2) normal = -normal; Diff_Geom *out = new Diff_Geom(ray_.at(t1), normal, t1); bounds.add(in, out); return true; } else { return false; } } else { return false; } }
// no one needs teh intersection point for the aabb, // it's just a boolean check //bool intersects( const Ray& ray, Intersection* intn ) ; bool AABB::intersectsRay( const Ray& ray, Vector3f& pt ) const { bool rayStartInside = containsPoint( ray.start ) ; // Rays starting and ending in an octree node must be considered // to hit the node. // This is the behavior you USUALLY want. if( rayStartInside && containsPoint( ray.end ) ) { pt=ray.start; return true ; } // the algorithm says, find 3 t's, Vector3f t ; if( rayStartInside ) { for( int i = 0 ; i < 3 ; i++ ) { if( ray.dir.elts[i] > 0 ) // RAY GOING + and we are inside the box.. CULL BACK FACE (mins) { t.elts[i] = ( max.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; } else // RAY GOING - and we are inside the box.. so cull the MAXES { t.elts[i] = ( min.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; } } } else { // LARGEST t is the only only we need to test if it's on the face. for( int i = 0 ; i < 3 ; i++ ) { if( ray.dir.elts[i] > 0 ) // RAY GOING +.. CULL BACK FACE (maxes) { // ^ | // / | // * | // -----+------- // | // A ray with ray.dir.x>0 means IT WOULD HIT MINFACES FIRST. // __BUT__, if the ray STARTS AFTER THE MINFACE (ray.start.x > min.x) // THEN THE RAY STARTED INSIDE THE BOX. to still detect the intersection // you would use the MAX FACE in that case t.elts[i] = ( min.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; } else // RAY GOING -.. so cull the MINS { // IF RAY LEFT OF MAXFACE, USE MINFACE t.elts[i] = ( max.elts[i] - ray.start.elts[i] ) / ray.dir.elts[i] ; } // The above code works fine for when you are between min/max in 2 dimensions or less, // because the detected hits for those dimensions will be BEHIND THE RAY START // so it's kind of like "using a loophole".. if you are between max/min in 2 dimensions, // THEN YOU CAN ONLY HIT THE BOX IN THE DIMENSION YOU ARE __NOT__ between max/min in, // unless you're already inside the box, which is hte cases handled above. } } // The right answer will be int tIndex ; if( rayStartInside ) tIndex = t.minIndex() ; else tIndex = t.maxIndex() ; if( isBetweenOrdered( t.elts[tIndex], 0, ray.len ) ) { pt = ray.at( t.elts[tIndex] ) ; // check it's in the box in other 2 dimensions int o1 = OTHERAXIS1(tIndex) ; // i=0: o1=1, o2=2, i=1: o1=2,o2=0 etc. int o2 = OTHERAXIS2(tIndex) ; return isBetweenOrdered( pt.elts[o1], min.elts[o1], max.elts[o1] ) && isBetweenOrdered( pt.elts[o2], min.elts[o2], max.elts[o2] ) ; } return false ; }