bool Quadric::intersect(const Point3D &eye, const Point3D &_ray, HitReporter &hr) const { Polynomial<2> x, y, z; const Vector3D ray = _ray - eye; x[0] = eye[0]; x[1] = ray[0]; y[0] = eye[1]; y[1] = ray[1]; z[0] = eye[2]; z[1] = ray[2]; const Polynomial<2> eqn = A * x * x + B * x * y + C * x * z + D * y * y + E * y * z + F * z * z + G * x + H * y + J * z + K; double ts[2]; auto nhits = eqn.solve(ts); if(nhits > 0) { const Polynomial<2> ddx = 2 * A * x + B * y + C * z + G, ddy = 2 * D * y + B * x + E * z + H, ddz = 2 * F * z + C * x + E * y + J; for(int i = 0; i < nhits; i++) { // Figure out the normal. const double t = ts[i]; const Point3D pt = eye + t * ray; if(!predicate(pt)) continue; Vector3D normal(ddx.eval(t), ddy.eval(t), ddz.eval(t)); // Figure out the uv. Point2D uv; Vector3D u, v; get_uv(pt, normal, uv, u, v); if(!hr.report(ts[i], normal, uv, u, v)) return false; } } return true; }
// This is basically the same as Quadric::intersect, but Quadric::intersect is // too general to be fast, so we're reimplementing it here. bool Sphere::intersect(const Point3D &eye, const Point3D &dst, HitReporter &hr) const { Polynomial<2> x, y, z; const Vector3D ray = dst - eye; x[0] = eye[0]; x[1] = ray[0]; y[0] = eye[1]; y[1] = ray[1]; z[0] = eye[2]; z[1] = ray[2]; const Polynomial<2> eqn = x * x + y * y + z * z + (-1); double ts[2]; auto nhits = eqn.solve(ts); if(nhits > 0) { for(int i = 0; i < nhits; i++) { const double t = ts[i]; const Point3D pt = eye + t * ray; // The normal is just the intersection point in the sphere's coordinate // system. We can avoid the normalize since this is a unit sphere. Vector3D normal(pt - Point3D()); // Figure out the uv. const double theta = atan2(-pt[2], pt[0]); const double y = pt[1]; const Point2D uv(0.5 + theta / 2 / M_PI, 0.5 + y * 0.5); Vector3D u = Vector3D(pt[1], 0, -pt[0]); u.normalize(); const Vector3D v = normal.cross(u); if(!hr.report(ts[i], normal, uv, u, v)) return false; } } return true; }
bool Cylinder::intersect(const Point3D &eye, const Point3D &dst, HitReporter &hr) const { // Part 1: test for intersection with the non-planar surface using the // cylinder equation. Polynomial<2> x, y, z; const Vector3D ray = dst - eye; x[0] = eye[0]; x[1] = ray[0]; y[0] = eye[1]; y[1] = ray[1]; z[0] = eye[2]; z[1] = ray[2]; const Polynomial<2> eqn = x * x + y * y + (-1); double ts[2]; auto nhits = eqn.solve(ts); if(nhits > 0) { for(int i = 0; i < nhits; i++) { const double t = ts[i]; const Point3D pt = eye + t * ray; if(!predicate(pt)) continue; const Vector3D normal(pt - Point3D(0, 0, pt[2])); // Figure out the uv. Vector3D u, v; Point2D uv; get_uv(pt, normal, uv, u, v); if(!hr.report(ts[i], normal, uv, u, v)) return false; } } // Next, test intersection with the two planar circle surfaces. const int circles[] = {AAFACE_PZ, AAFACE_NZ}; const double offsets[] = {1., -1.}; for(int i = 0; i < NUMELMS(circles); i++) { const int face = circles[i]; const int axis = face / 2; const double offset = offsets[i]; const double t = axis_aligned_plane_intersect(eye, ray, axis, offset); if(t < numeric_limits<double>::max()) { const Point3D p = eye + t * ray; if(axis_aligned_circle_contains(p, face, 1.)) { Vector3D normal; normal[axis] = offset; const double _u = 0.25 * ((face == AAFACE_PZ ? 1 : 3) + p[0]); const double _v = 0.25 * (3 + p[1]); Point2D uv(_u, _v); Vector3D u(1, 0, 0); Vector3D v(0, 1, 0); if(!hr.report(t, normal, uv, u, v)) return false; } } } return true; }
RootFinding::Roots RootFinding::solve(const Polynomial &p,const Polynomial &q) { return RootFinding::Roots(p.solve(q)); }