void Octree::_FattenedRayLookup(int nindex,const Ray3D& ray,Real radius,vector<int>& nodeindices) const { const OctreeNode& node = nodes[nindex]; //this is kinda slower than it needs to be... can determine intersecting //children directly without testing all bb's if(IsLeaf(node)) { //fine grained test if(ray.distance(node.bb) <= radius) nodeindices.push_back(nindex); } else { vector<pair<Real,int> > children; for(int i=0;i<8;i++) { Real tmin = 0; Real tmax = Inf; AABB3D fattened = nodes[node.childIndices[i]].bb; fattened.bmin -= Vector3(radius); fattened.bmax += Vector3(radius); if(ray.intersects(fattened,tmin,tmax)) children.push_back(pair<Real,int>(tmin,node.childIndices[i])); } //loop through children, sorted by distance sort(children.begin(),children.end()); for(size_t i=0;i<children.size();i++) _FattenedRayLookup(children[i].second,ray,radius,nodeindices); } }
bool RayTracer::render () { Ray3D ray; ray.setOrigin (Vector3 (0, 0, 5)); float sigmaX = static_cast<float> (_left); float sigmaY = static_cast<float> (_top); for (int curScanLine = 0; curScanLine < _bitmap.getHeight (); curScanLine++) { for (int x = 0; x < _bitmap.getWidth (); x++) { ray.setDirection (Vector3 (sigmaX, sigmaY, 0) - ray.getOrigin ()); //no need to normalize - Ray3D does this automatically for us! Color pixelColor = traceRay (ray, 999999999.0f); if (_bitmap.putPixel (x, curScanLine, pixelColor) != E_SUCCESS) { return (_done = false); } sigmaX += _deltaX; } sigmaX = static_cast<float> (_left); sigmaY += _deltaY; } _done = true; return _done; }
static bool F_Hit_Translate_Handle_At_Center( APlaceable* o, const EdSceneViewport& viewport ) { const Ray3D eyeRay = GetEyeRay(viewport); const FLOAT dist = eyeRay.Distance( o->GetOrigin() ); const FLOAT pickingDist = F_Get_Translate_Handle_Radius( eyeRay.origin, o->GetOrigin() ); return dist < pickingDist; }
bool Ray3DRect3DIntersection(const Rect3D & rect, const Ray3D & ray, float & t) { return Ray3DRect3DIntersection( rect.origin(), rect.right(), rect.up(), ray.origin(), ray.direction(), t); }
glm::vec2 Rect3DRay3DIntersectionPoint( const Rect3D & rect, const Ray3D & ray, bool & valid) { return Rect3DRay3DIntersectionPoint( rect.origin(), rect.right(), rect.up(), ray.origin(), ray.direction(), valid); }
void Ray3D::closestPoint(const Ray3D& r,Real& t,Real& u) const { Line3D::closestPoint(r,t,u); if(t < 0) { t=0; u=r.closestPointParameter(source); } if(u < 0) { u=0; t=r.closestPointParameter(r.source); } if(t < 0) t=0; }
/** * A ray is represented by l0 + l * t = p * l0 - ray origin * l - ray direction * t - parameter * p - point on plane * A plane is represented by (p - p0) dot n = 0 (because perpendicular) * p0 - a point representing the distance from the origin * n - normal of plane * Solving for t by substituting p gives that * t = [ (p0 - l0) dot n ] / [ l dot n ] */ double Plane::findIntersection(const Ray3D & ray) const { Vector3D rayDirection = ray.getDirection(); Vector3D l = rayDirection; Vector3D n = normal; double ldotn = l.dotProduct(n); if (0 == ldotn) { // Ray is || to plane return -1; // Never intersects } else { Vector3D p0 = normal * distance; Vector3D l0 = ray.getOrigin(); double numerator = (p0 - l0).dotProduct(n); return numerator / ldotn; } }
int OctreePointSet::RayCast(const Ray3D& r,Real radius) const { vector<int> raynodes; FattenedRayLookup(r,radius,raynodes); Vector3 temp; Real r2 = radius*radius; for(size_t i=0;i<raynodes.size();i++) { const vector<Vector3>& pts = pointLists[raynodes[i]]; const vector<int>& bids = idLists[raynodes[i]]; Real closest = Inf; int result = -1; for(size_t k=0;k<pts.size();k++) { Real t = r.closestPoint(pts[k],temp); if(pts[k].distanceSquared(temp) <= r2) { if(t < closest) { closest = t; result = bids[k]; } } } if(result >= 0) return result; } return -1; }
bool Triangle3::intersectsLocal(const Ray3D& ray) const { Vector3D n = faceNormal(); double nd = n.dot(ray.direction); if (nd < std::numeric_limits<double>::epsilon()) { return false; } double d = n.dot(points[0]); double t = (d - n.dot(ray.origin)) / nd; if (t < 0.0) { return false; } Vector3D q = ray.pointAt(t); Vector3D q01 = (points[1] - points[0]).cross(q - points[0]); if (n.dot(q01) <= 0.0) { return false; } Vector3D q12 = (points[2] - points[1]).cross(q - points[1]); if (n.dot(q12) <= 0.0) { return false; } Vector3D q02 = (points[0] - points[2]).cross(q - points[2]); if (n.dot(q02) <= 0.0) { return false; } return true; }
void Octree::RayLookup(const Ray3D& ray,vector<int>& nodeindices) const { nodeindices.resize(0); Real tmin=0,tmax=Inf; if(!ray.intersects(nodes[0].bb,tmin,tmax)) return; _RayLookup(0,ray,nodeindices); }
void Octree::FattenedRayLookup(const Ray3D& ray,Real radius,vector<int>& nodeindices) const { nodeindices.resize(0); Real tmin=0,tmax=Inf; AABB3D fattened = nodes[0].bb; fattened.bmin -= Vector3(radius); fattened.bmax += Vector3(radius); if(!ray.intersects(fattened,tmin,tmax)) return; _FattenedRayLookup(0,ray,radius,nodeindices); }
SurfaceRayIntersection3 Triangle3::closestIntersectionLocal( const Ray3D& ray) const { SurfaceRayIntersection3 intersection; Vector3D n = faceNormal(); double nd = n.dot(ray.direction); if (nd < std::numeric_limits<double>::epsilon()) { intersection.isIntersecting = false; return intersection; } double d = n.dot(points[0]); double t = (d - n.dot(ray.origin)) / nd; if (t < 0.0) { intersection.isIntersecting = false; return intersection; } Vector3D q = ray.pointAt(t); Vector3D q01 = (points[1] - points[0]).cross(q - points[0]); if (n.dot(q01) <= 0.0) { intersection.isIntersecting = false; return intersection; } Vector3D q12 = (points[2] - points[1]).cross(q - points[1]); if (n.dot(q12) <= 0.0) { intersection.isIntersecting = false; return intersection; } Vector3D q02 = (points[0] - points[2]).cross(q - points[2]); if (n.dot(q02) <= 0.0) { intersection.isIntersecting = false; return intersection; } double a = area(); double b0 = 0.5 * q12.length() / a; double b1 = 0.5 * q02.length() / a; double b2 = 0.5 * q01.length() / a; Vector3D normal = b0 * normals[0] + b1 * normals[1] + b2 * normals[2]; intersection.isIntersecting = true; intersection.distance = t; intersection.point = q; intersection.normal = normal.normalized(); return intersection; }
Color RayTracer::traceRay (Ray3D ray, float distance) { Primitive* closestPrim = NULL; float newDistance; Color color (0.0, 0.0, 0.0); for (int i = 0; i < _scene->getNumberOfPrimitives (); i++) { newDistance = _scene->getPrimitive (i)->findIntersectionWith (ray, distance); if ((newDistance >= 0.0) && (newDistance < distance)) { closestPrim = _scene->getPrimitive (i); distance = newDistance; } } if (closestPrim == NULL) { return color; } if (closestPrim->getIsLight ()) { color = closestPrim->getColor (); return color; } // calculate diffuse lighting Vector3 pointOfIntersection = ray.getDirection () * distance + ray.getOrigin (); Vector3 normal = closestPrim->getNormalAt (pointOfIntersection); for (int i = 0; i < _scene->getNumberOfPrimitives (); i++) { Primitive* light = _scene->getPrimitive (i); if (!light->getIsLight ()) { // wait, this isn't actually a light ... continue; } Vector3 lightDir = light->getOrigin () - pointOfIntersection; lightDir.normalize(); float lightCoef = normal.dotProduct (lightDir); color = color + closestPrim->getColor () * light->getColor () * lightCoef; } return color; }
bool UVSphere::shadowHit(const Ray3D& r, float tmin, float tmax, float time) const { Vector3D temp = r.getOrigin() - center; double a = dotProduct(r.getDirection(), r.getDirection()); double b = 2*dotProduct(r.getDirection(), temp); double c = dotProduct(temp, temp) - radius*radius; double disc = b*b -4*a*c; //is there some intersection if(disc > 0.0) { disc = sqrt(disc); double t = (-b - disc) / (2.0*a); if (t < tmin) t = (-b + disc) /(2.0*a); if (t < tmin || t > tmax) return false; return true; } return false; }
// Return distance from ray origin to intersection // See comments for variables and the equations in Plane.cpp's findIntersection double Triangle::findIntersection(const Ray3D & ray) const { // See if the ray intersects the bounding box // *** With bounding box if (!Object::intersectsBBox(ray)) { return -1; } // First check if the ray intersects with the plane (use same calculations) Vector3D rayDirection = ray.getDirection(); Vector3D rayOrigin = ray.getOrigin(); double ldotn = rayDirection.dotProduct(normal); if (0 == ldotn) { // Ray is || to triangle return -1; } else { Vector3D p0 = normal * distance; double distanceToPlane = (p0 - rayOrigin).dotProduct(normal) / ldotn; // Then see if the point is inside the triangle (3 conditions) // Q is the point of intersection Vector3D Q = (rayDirection * distanceToPlane) + rayOrigin; Vector3D sideCA = epC - epA; Vector3D segQA = Q - epA; // 1. (CA x QA) * n >= 0 if (sideCA.crossProduct(segQA).dotProduct(normal) < 0) { return -1; } Vector3D sideBC = epB - epC; Vector3D segQC = Q - epC; // 2. (BC x QC) * n >= 0 if (sideBC.crossProduct(segQC).dotProduct(normal) < 0) { return -1; } Vector3D sideAB = epA - epB; Vector3D segQB = Q - epB; // 3. (AB x QB) * n >= 0 if (sideAB.crossProduct(segQB).dotProduct(normal) < 0) { return -1; } return distanceToPlane; } }
SurfaceRayIntersection3 ImplicitSurfaceSet3::closestIntersectionLocal( const Ray3D& ray) const { buildBvh(); const auto testFunc = [](const Surface3Ptr& surface, const Ray3D& ray) { SurfaceRayIntersection3 result = surface->closestIntersection(ray); return result.distance; }; const auto queryResult = _bvh.closestIntersection(ray, testFunc); SurfaceRayIntersection3 result; result.distance = queryResult.distance; result.isIntersecting = queryResult.item != nullptr; if (queryResult.item != nullptr) { result.point = ray.pointAt(queryResult.distance); result.normal = (*queryResult.item)->closestNormal(result.point); } return result; }
void OctreePointSet::RayQuery(const Ray3D& r,Real radius,vector<Vector3>& points,vector<int>& ids) const { points.resize(0); ids.resize(0); vector<int> raynodes; FattenedRayLookup(r,radius,raynodes); Vector3 temp; Real r2 = radius*radius; for(size_t i=0;i<raynodes.size();i++) { const vector<Vector3>& pts = pointLists[raynodes[i]]; const vector<int>& bids = idLists[raynodes[i]]; for(size_t k=0;k<pts.size();k++) { r.closestPoint(pts[k],temp); if(pts[k].distanceSquared(temp) <= r2) { points.push_back(pts[k]); ids.push_back(bids[k]); } } } }
void Octree::_RayLookup(int nindex,const Ray3D& ray,vector<int>& nodeindices) const { const OctreeNode& node = nodes[nindex]; //this is kinda slower than it needs to be... can determine intersecting //children directly without testing all bb's if(IsLeaf(node)) nodeindices.push_back(nindex); else { vector<pair<Real,int> > children; for(int i=0;i<8;i++) { Real tmin = 0; Real tmax = Inf; if(ray.intersects(nodes[node.childIndices[i]].bb,tmin,tmax)) children.push_back(pair<Real,int>(tmin,node.childIndices[i])); } //loop through children, sorted by distance sort(children.begin(),children.end()); for(size_t i=0;i<children.size();i++) _RayLookup(children[i].second,ray,nodeindices); } }
bool UVSphere::hit(const Ray3D& r, float tmin, float tmax, float time, HitRecord& record) const { Vector3D temp = r.getOrigin() - center; double a = dotProduct(r.getDirection(), r.getDirection()); double b = 2*dotProduct(r.getDirection(), temp); double c = dotProduct(temp, temp) - radius*radius; double disc = b*b -4*a*c; //is there some intersection if(disc > 0.0) { disc = sqrt(disc); double t = (-b - disc) / (2.0*a); if (t < tmin) t = (-b + disc) /(2.0*a); if (t < tmin || t > tmax) return false; record.t = t; record.p = record.texp = (r.getOrigin() + t*r.getDirection()); record.uvw.initFromW((record.p - center) / radius); Vector3D n = (record.p - center) / radius; float twopi = 6.28318530718f; float theta = acos(n.getZ()); float phi = atan2(n.getY(), n.getX()); if (phi < 0.0f) phi+= twopi; float one_over_2pi = .159154943029f; float one_over_pi = .318309886184f; float pi = 3.14159; record.uv = Vector2D(phi*one_over_2pi, (pi-theta)*one_over_pi); record.mat_ptr = material; return true; } return false; }
bool TransformWidget::Hover(int x,int y,Camera::Viewport& viewport,double& distance) { Real globalScale = 1.0; if(scaleToScreen) { float sx,sy,sz; viewport.project(T.t,sx,sy,sz); globalScale = sz/viewport.scale; } distance = Inf; int oldHoverItem = hoverItem; hoverItem = -1; Ray3D r; viewport.getClickSource(x,y,r.source); viewport.getClickVector(x,y,r.direction); //check origin if(enableTranslation && enableOriginTranslation) { Sphere3D s; s.center = T.t; s.radius = originRadius*globalScale; Real tmin,tmax; if(s.intersects(r,&tmin,&tmax)) { distance = tmin; hoverItem = 0; } } //check translation axes for(int i=0;i<3;i++) { if(!enableTranslation) break; if(!enableTranslationAxes[i]) continue; Line3D axisLine; axisLine.source = T.t; axisLine.direction = Vector3(T.R.col(i)); Real t,u; axisLine.closestPoint(r,t,u); t = Clamp(t,0.0,axisLength*globalScale); u = Clamp(u,0.0,Inf); Vector3 paxis,pray; axisLine.eval(t,paxis); r.eval(u,pray); if(paxis.distanceSquared(pray) <= Sqr(axisRadius*globalScale)) { if(u < distance) { distance = u; hoverItem = 1+i; } } } if(enableRotation) { //check rotation rings Circle3D c; c.center = T.t; for(int i=0;i<3;i++) { if(!enableRotationAxes[i]) continue; c.axis = Vector3(T.R.col(i)); c.radius = ringOuterRadius*globalScale; Real t; if(c.intersects(r,&t) && t >= 0) { c.radius = ringInnerRadius*globalScale; if(!c.intersects(r,NULL)) { if(t < distance) { distance = t; hoverItem = i+4; } } } } } if(enableRotation && enableOuterRingRotation) { //check outer ring Circle3D c; c.center = T.t; viewport.getViewVector(c.axis); c.radius = (ringOuterRadius+arrowHeight)*globalScale; Real t; if(c.intersects(r,&t) && t >= 0) { c.radius = (ringInnerRadius+arrowHeight)*globalScale; if(!c.intersects(r,NULL)) { if(t < distance) { distance = t; hoverItem = 7; } } } clickAxis = c.axis; } if(hoverItem != oldHoverItem) Refresh(); r.eval(distance,hoverPos); return hoverItem != -1; }
TEST(Ray3, PointAt) { Ray3D ray; EXPECT_EQ(Vector3D(4.5, 0.0, 0.0), ray.pointAt(4.5)); }
bool Intersect(const Ray3D & ray, const Sphere & sphere) { auto nray = ray.normalized(); return NormalizedRay3DSphereIntersection(nray.origin(), nray.direction(), sphere.position(), sphere.radius()); }