bool Disk::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect) const { // Transform _Ray_ to object space Vector3f oErr, dErr; Ray ray = (*WorldToObject)(r, &oErr, &dErr); // Compute plane intersection for disk // Reject disk intersections for rays parallel to the disk's plane if (ray.d.z == 0) return false; Float tShapeHit = (height - ray.o.z) / ray.d.z; if (tShapeHit <= 0 || tShapeHit >= ray.tMax) return false; // See if hit point is inside disk radii and $\phimax$ Point3f pHit = ray(tShapeHit); Float dist2 = pHit.x * pHit.x + pHit.y * pHit.y; if (dist2 > radius * radius || dist2 < innerRadius * innerRadius) return false; // Refine disk intersection point pHit.z = height; // Test disk $\phi$ value against $\phimax$ Float phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; if (phi > phiMax) return false; // Find parametric representation of disk hit Float u = phi / phiMax; Float rHit = std::sqrt(dist2); Float oneMinusV = ((rHit - innerRadius) / (radius - innerRadius)); Float v = 1 - oneMinusV; Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0.); Vector3f dpdv = Vector3f(pHit.x, pHit.y, 0.) * (innerRadius - radius) / rHit; Normal3f dndu(0, 0, 0), dndv(0, 0, 0); // Compute error bounds for disk intersection Vector3f pError(0., 0., 0.); // Initialize _SurfaceInteraction_ from parametric information *isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v), -ray.d, dpdu, dpdv, dndu, dndv, ray.time, this)); // Update _tHit_ for quadric intersection *tHit = (Float)tShapeHit; return true; }
bool Cylinder::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect, bool testAlphaTexture) const { Float phi; Point3f pHit; // Transform _Ray_ to object space Vector3f oErr, dErr; Ray ray = (*WorldToObject)(r, &oErr, &dErr); // Compute quadratic cylinder coefficients // Initialize _EFloat_ ray coordinate values EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z); EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z); EFloat a = dx * dx + dy * dy; EFloat b = 2 * (dx * ox + dy * oy); EFloat c = ox * ox + oy * oy - EFloat(radius) * EFloat(radius); // Solve quadratic equation for _t_ values EFloat t0, t1; if (!Quadratic(a, b, c, &t0, &t1)) return false; // Check quadric shape _t0_ and _t1_ for nearest intersection if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false; EFloat tShapeHit = t0; if (tShapeHit.LowerBound() <= 0) { tShapeHit = t1; if (tShapeHit.UpperBound() > ray.tMax) return false; } // Compute cylinder hit point and $\phi$ pHit = ray((Float)tShapeHit); // Refine cylinder intersection point Float hitRad = std::sqrt(pHit.x * pHit.x + pHit.y * pHit.y); pHit.x *= radius / hitRad; pHit.y *= radius / hitRad; phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; // Test cylinder intersection against clipping parameters if (pHit.z < zMin || pHit.z > zMax || phi > phiMax) { if (tShapeHit == t1) return false; tShapeHit = t1; if (t1.UpperBound() > ray.tMax) return false; // Compute cylinder hit point and $\phi$ pHit = ray((Float)tShapeHit); // Refine cylinder intersection point Float hitRad = std::sqrt(pHit.x * pHit.x + pHit.y * pHit.y); pHit.x *= radius / hitRad; pHit.y *= radius / hitRad; phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; if (pHit.z < zMin || pHit.z > zMax || phi > phiMax) return false; } // Find parametric representation of cylinder hit Float u = phi / phiMax; Float v = (pHit.z - zMin) / (zMax - zMin); // Compute cylinder $\dpdu$ and $\dpdv$ Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0); Vector3f dpdv(0, 0, zMax - zMin); // Compute cylinder $\dndu$ and $\dndv$ Vector3f d2Pduu = -phiMax * phiMax * Vector3f(pHit.x, pHit.y, 0); Vector3f d2Pduv(0, 0, 0), d2Pdvv(0, 0, 0); // Compute coefficients for fundamental forms Float E = Dot(dpdu, dpdu); Float F = Dot(dpdu, dpdv); Float G = Dot(dpdv, dpdv); Vector3f N = Normalize(Cross(dpdu, dpdv)); Float e = Dot(N, d2Pduu); Float f = Dot(N, d2Pduv); Float g = Dot(N, d2Pdvv); // Compute $\dndu$ and $\dndv$ from fundamental form coefficients Float invEGF2 = 1 / (E * G - F * F); Normal3f dndu = Normal3f((f * F - e * G) * invEGF2 * dpdu + (e * F - f * E) * invEGF2 * dpdv); Normal3f dndv = Normal3f((g * F - f * G) * invEGF2 * dpdu + (f * F - g * E) * invEGF2 * dpdv); // Compute error bounds for cylinder intersection Vector3f pError = gamma(3) * Abs(Vector3f(pHit.x, pHit.y, 0)); // Initialize _SurfaceInteraction_ from parametric information *isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v), -ray.d, dpdu, dpdv, dndu, dndv, ray.time, this)); // Update _tHit_ for quadric intersection *tHit = (Float)tShapeHit; return true; }
bool Curve::recursiveIntersect(const Ray &ray, Float *tHit, SurfaceInteraction *isect, const Point3f cp[4], const Transform &rayToObject, Float u0, Float u1, int depth) const { // Try to cull curve segment versus ray // Compute bounding box of curve segment, _curveBounds_ Bounds3f curveBounds = Union(Bounds3f(cp[0], cp[1]), Bounds3f(cp[2], cp[3])); Float maxWidth = std::max(Lerp(u0, common->width[0], common->width[1]), Lerp(u1, common->width[0], common->width[1])); curveBounds = Expand(curveBounds, 0.5 * maxWidth); // Compute bounding box of ray, _rayBounds_ Float rayLength = ray.d.Length(); Float zMax = rayLength * ray.tMax; Bounds3f rayBounds(Point3f(0, 0, 0), Point3f(0, 0, zMax)); if (Overlaps(curveBounds, rayBounds) == false) return false; if (depth > 0) { // Split curve segment into sub-segments and test for intersection Float uMid = 0.5f * (u0 + u1); Point3f cpSplit[7]; SubdivideBezier(cp, cpSplit); return (recursiveIntersect(ray, tHit, isect, &cpSplit[0], rayToObject, u0, uMid, depth - 1) || recursiveIntersect(ray, tHit, isect, &cpSplit[3], rayToObject, uMid, u1, depth - 1)); } else { // Intersect ray with curve segment // Test ray against segment endpoint boundaries Vector2f segmentDirection = Point2f(cp[3]) - Point2f(cp[0]); // Test sample point against tangent perpendicular at curve start Float edge = (cp[1].y - cp[0].y) * -cp[0].y + cp[0].x * (cp[0].x - cp[1].x); if (edge < 0) return false; // Test sample point against tangent perpendicular at curve end // TODO: update to match the starting test. Vector2f endTangent = Point2f(cp[2]) - Point2f(cp[3]); if (Dot(segmentDirection, endTangent) < 0) endTangent = -endTangent; if (Dot(endTangent, Vector2f(cp[3])) < 0) return false; // Compute line $w$ that gives minimum distance to sample point Float denom = Dot(segmentDirection, segmentDirection); if (denom == 0) return false; Float w = Dot(-Vector2f(cp[0]), segmentDirection) / denom; // Compute $u$ coordinate of curve intersection point and _hitWidth_ Float u = Clamp(Lerp(w, u0, u1), u0, u1); Float hitWidth = Lerp(u, common->width[0], common->width[1]); Normal3f nHit; if (common->type == CurveType::Ribbon) { // Scale _hitWidth_ based on ribbon orientation Float sin0 = std::sin((1 - u) * common->normalAngle) * common->invSinNormalAngle; Float sin1 = std::sin(u * common->normalAngle) * common->invSinNormalAngle; nHit = sin0 * common->n[0] + sin1 * common->n[1]; hitWidth *= AbsDot(nHit, -ray.d / rayLength); } // Test intersection point against curve width Vector3f dpcdw; Point3f pc = EvalBezier(cp, Clamp(w, 0, 1), &dpcdw); Float ptCurveDist2 = pc.x * pc.x + pc.y * pc.y; if (ptCurveDist2 > hitWidth * hitWidth * .25) return false; if (pc.z < 0 || pc.z > zMax) return false; // Compute $v$ coordinate of curve intersection point Float ptCurveDist = std::sqrt(ptCurveDist2); Float edgeFunc = dpcdw.x * -pc.y + pc.x * dpcdw.y; Float v = (edgeFunc > 0.) ? 0.5f + ptCurveDist / hitWidth : 0.5f - ptCurveDist / hitWidth; // Compute hit _t_ and partial derivatives for curve intersection if (tHit != nullptr) { // FIXME: this tHit isn't quite right for ribbons... *tHit = pc.z / rayLength; // Compute error bounds for curve intersection Vector3f pError(2 * hitWidth, 2 * hitWidth, 2 * hitWidth); // Compute $\dpdu$ and $\dpdv$ for curve intersection Vector3f dpdu, dpdv; EvalBezier(common->cpObj, u, &dpdu); if (common->type == CurveType::Ribbon) dpdv = Normalize(Cross(nHit, dpdu)) * hitWidth; else { // Compute curve $\dpdv$ for flat and cylinder curves Vector3f dpduPlane = (Inverse(rayToObject))(dpdu); Vector3f dpdvPlane = Normalize(Vector3f(-dpduPlane.y, dpduPlane.x, 0)) * hitWidth; if (common->type == CurveType::Cylinder) { // Rotate _dpdvPlane_ to give cylindrical appearance Float theta = Lerp(v, -90., 90.); Transform rot = Rotate(-theta, dpduPlane); dpdvPlane = rot(dpdvPlane); } dpdv = rayToObject(dpdvPlane); } *isect = (*ObjectToWorld)(SurfaceInteraction( ray(pc.z), pError, Point2f(u, v), -ray.d, dpdu, dpdv, Normal3f(0, 0, 0), Normal3f(0, 0, 0), ray.time, this)); } ++nHits; return true; } }
bool Hyperboloid::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect) const { Float phi, v; Point3f pHit; // Transform _Ray_ to object space Vector3f oErr, dErr; Ray ray = (*WorldToObject)(r, &oErr, &dErr); // Compute quadratic hyperboloid coefficients // Initialize _EFloat_ ray coordinate values EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z); EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z); EFloat a = ah * dx * dx + ah * dy * dy - ch * dz * dz; EFloat b = 2.f * (ah * dx * ox + ah * dy * oy - ch * dz * oz); EFloat c = ah * ox * ox + ah * oy * oy - ch * oz * oz - 1.f; // Solve quadratic equation for _t_ values EFloat t0, t1; if (!Quadratic(a, b, c, &t0, &t1)) return false; // Check quadric shape _t0_ and _t1_ for nearest intersection if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false; EFloat tShapeHit = t0; if (t0.LowerBound() <= 0) { tShapeHit = t1; if (tShapeHit.UpperBound() > ray.tMax) return false; } // Compute hyperboloid inverse mapping pHit = ray((Float)tShapeHit); v = (pHit.z - p1.z) / (p2.z - p1.z); Point3f pr = (1 - v) * p1 + v * p2; phi = std::atan2(pr.x * pHit.y - pHit.x * pr.y, pHit.x * pr.x + pHit.y * pr.y); if (phi < 0) phi += 2 * Pi; // Test hyperboloid intersection against clipping parameters if (pHit.z < zMin || pHit.z > zMax || phi > phiMax) { if (tShapeHit == t1) return false; tShapeHit = t1; if (t1.UpperBound() > ray.tMax) return false; // Compute hyperboloid inverse mapping pHit = ray((Float)tShapeHit); v = (pHit.z - p1.z) / (p2.z - p1.z); Point3f pr = (1 - v) * p1 + v * p2; phi = std::atan2(pr.x * pHit.y - pHit.x * pr.y, pHit.x * pr.x + pHit.y * pr.y); if (phi < 0) phi += 2 * Pi; if (pHit.z < zMin || pHit.z > zMax || phi > phiMax) return false; } // Compute parametric representation of hyperboloid hit Float u = phi / phiMax; // Compute hyperboloid $\dpdu$ and $\dpdv$ Float cosPhi = std::cos(phi), sinPhi = std::sin(phi); Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0.); Vector3f dpdv((p2.x - p1.x) * cosPhi - (p2.y - p1.y) * sinPhi, (p2.x - p1.x) * sinPhi + (p2.y - p1.y) * cosPhi, p2.z - p1.z); // Compute hyperboloid $\dndu$ and $\dndv$ Vector3f d2Pduu = -phiMax * phiMax * Vector3f(pHit.x, pHit.y, 0); Vector3f d2Pduv = phiMax * Vector3f(-dpdv.y, dpdv.x, 0.); Vector3f d2Pdvv(0, 0, 0); // Compute coefficients for fundamental forms Float E = Dot(dpdu, dpdu); Float F = Dot(dpdu, dpdv); Float G = Dot(dpdv, dpdv); Vector3f N = Normalize(Cross(dpdu, dpdv)); Float e = Dot(N, d2Pduu); Float f = Dot(N, d2Pduv); Float g = Dot(N, d2Pdvv); // Compute $\dndu$ and $\dndv$ from fundamental form coefficients Float invEGF2 = 1 / (E * G - F * F); Normal3f dndu = Normal3f((f * F - e * G) * invEGF2 * dpdu + (e * F - f * E) * invEGF2 * dpdv); Normal3f dndv = Normal3f((g * F - f * G) * invEGF2 * dpdu + (f * F - g * E) * invEGF2 * dpdv); // Compute error bounds for hyperboloid intersection // Compute error bounds for intersection computed with ray equation EFloat px = ox + tShapeHit * dx; EFloat py = oy + tShapeHit * dy; EFloat pz = oz + tShapeHit * dz; Vector3f pError = Vector3f(px.GetAbsoluteError(), py.GetAbsoluteError(), pz.GetAbsoluteError()); // Initialize _SurfaceInteraction_ from parametric information *isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v), -ray.d, dpdu, dpdv, dndu, dndv, ray.time, this)); *tHit = (Float)tShapeHit; return true; }
bool Paraboloid::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect) const { Float phi; Point3f pHit; // Transform _Ray_ to object space Vector3f oErr, dErr; Ray ray = (*WorldToObject)(r, &oErr, &dErr); // Compute quadratic paraboloid coefficients // Initialize _EFloat_ ray coordinate values EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z); EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z); EFloat k = EFloat(zMax) / (EFloat(radius) * EFloat(radius)); EFloat a = k * (dx * dx + dy * dy); EFloat b = 2.f * k * (dx * ox + dy * oy) - dz; EFloat c = k * (ox * ox + oy * oy) - oz; // Solve quadratic equation for _t_ values EFloat t0, t1; if (!Quadratic(a, b, c, &t0, &t1)) return false; // Check quadric shape _t0_ and _t1_ for nearest intersection if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false; EFloat tShapeHit = t0; if (tShapeHit.LowerBound() <= 0) { tShapeHit = t1; if (tShapeHit.UpperBound() > ray.tMax) return false; } // Compute paraboloid inverse mapping pHit = ray((Float)tShapeHit); phi = std::atan2(pHit.y, pHit.x); if (phi < 0.) phi += 2 * Pi; // Test paraboloid intersection against clipping parameters if (pHit.z < zMin || pHit.z > zMax || phi > phiMax) { if (tShapeHit == t1) return false; tShapeHit = t1; if (t1.UpperBound() > ray.tMax) return false; // Compute paraboloid inverse mapping pHit = ray((Float)tShapeHit); phi = std::atan2(pHit.y, pHit.x); if (phi < 0.) phi += 2 * Pi; if (pHit.z < zMin || pHit.z > zMax || phi > phiMax) return false; } // Find parametric representation of paraboloid hit Float u = phi / phiMax; Float v = (pHit.z - zMin) / (zMax - zMin); // Compute paraboloid $\dpdu$ and $\dpdv$ Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0.); Vector3f dpdv = (zMax - zMin) * Vector3f(pHit.x / (2 * pHit.z), pHit.y / (2 * pHit.z), 1.); // Compute paraboloid $\dndu$ and $\dndv$ Vector3f d2Pduu = -phiMax * phiMax * Vector3f(pHit.x, pHit.y, 0); Vector3f d2Pduv = (zMax - zMin) * phiMax * Vector3f(-pHit.y / (2 * pHit.z), pHit.x / (2 * pHit.z), 0); Vector3f d2Pdvv = -(zMax - zMin) * (zMax - zMin) * Vector3f(pHit.x / (4 * pHit.z * pHit.z), pHit.y / (4 * pHit.z * pHit.z), 0.); // Compute coefficients for fundamental forms Float E = Dot(dpdu, dpdu); Float F = Dot(dpdu, dpdv); Float G = Dot(dpdv, dpdv); Vector3f N = Normalize(Cross(dpdu, dpdv)); Float e = Dot(N, d2Pduu); Float f = Dot(N, d2Pduv); Float g = Dot(N, d2Pdvv); // Compute $\dndu$ and $\dndv$ from fundamental form coefficients Float invEGF2 = 1 / (E * G - F * F); Normal3f dndu = Normal3f((f * F - e * G) * invEGF2 * dpdu + (e * F - f * E) * invEGF2 * dpdv); Normal3f dndv = Normal3f((g * F - f * G) * invEGF2 * dpdu + (f * F - g * E) * invEGF2 * dpdv); // Compute error bounds for paraboloid intersection // Compute error bounds for intersection computed with ray equation EFloat px = ox + tShapeHit * dx; EFloat py = oy + tShapeHit * dy; EFloat pz = oz + tShapeHit * dz; Vector3f pError = Vector3f(px.GetAbsoluteError(), py.GetAbsoluteError(), pz.GetAbsoluteError()); // Initialize _SurfaceInteraction_ from parametric information *isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v), -ray.d, dpdu, dpdv, dndu, dndv, ray.time, this)); *tHit = (Float)tShapeHit; return true; }
bool Sphere::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect, bool testAlphaTexture) const { Float phi; Point3f pHit; // Transform _Ray_ to object space Vector3f oErr, dErr; Ray ray = (*WorldToObject)(r, &oErr, &dErr); // Compute quadratic sphere coefficients // Initialize _EFloat_ ray coordinate values EFloat ox(ray.o.x, oErr.x), oy(ray.o.y, oErr.y), oz(ray.o.z, oErr.z); EFloat dx(ray.d.x, dErr.x), dy(ray.d.y, dErr.y), dz(ray.d.z, dErr.z); EFloat a = dx * dx + dy * dy + dz * dz; EFloat b = 2 * (dx * ox + dy * oy + dz * oz); EFloat c = ox * ox + oy * oy + oz * oz - EFloat(radius) * EFloat(radius); // Solve quadratic equation for _t_ values EFloat t0, t1; if (!Quadratic(a, b, c, &t0, &t1)) return false; // Check quadric shape _t0_ and _t1_ for nearest intersection if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false; EFloat tShapeHit = t0; if (tShapeHit.LowerBound() <= 0) { tShapeHit = t1; if (tShapeHit.UpperBound() > ray.tMax) return false; } // Compute sphere hit position and $\phi$ pHit = ray((Float)tShapeHit); // Refine sphere intersection point pHit *= radius / Distance(pHit, Point3f(0, 0, 0)); if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius; phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; // Test sphere intersection against clipping parameters if ((zMin > -radius && pHit.z < zMin) || (zMax < radius && pHit.z > zMax) || phi > phiMax) { if (tShapeHit == t1) return false; if (t1.UpperBound() > ray.tMax) return false; tShapeHit = t1; // Compute sphere hit position and $\phi$ pHit = ray((Float)tShapeHit); // Refine sphere intersection point pHit *= radius / Distance(pHit, Point3f(0, 0, 0)); if (pHit.x == 0 && pHit.y == 0) pHit.x = 1e-5f * radius; phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; if ((zMin > -radius && pHit.z < zMin) || (zMax < radius && pHit.z > zMax) || phi > phiMax) return false; } // Find parametric representation of sphere hit Float u = phi / phiMax; Float theta = std::acos(Clamp(pHit.z / radius, -1, 1)); Float v = (theta - thetaMin) / (thetaMax - thetaMin); // Compute sphere $\dpdu$ and $\dpdv$ Float zRadius = std::sqrt(pHit.x * pHit.x + pHit.y * pHit.y); Float invZRadius = 1 / zRadius; Float cosPhi = pHit.x * invZRadius; Float sinPhi = pHit.y * invZRadius; Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0); Vector3f dpdv = (thetaMax - thetaMin) * Vector3f(pHit.z * cosPhi, pHit.z * sinPhi, -radius * std::sin(theta)); // Compute sphere $\dndu$ and $\dndv$ Vector3f d2Pduu = -phiMax * phiMax * Vector3f(pHit.x, pHit.y, 0); Vector3f d2Pduv = (thetaMax - thetaMin) * pHit.z * phiMax * Vector3f(-sinPhi, cosPhi, 0.); Vector3f d2Pdvv = -(thetaMax - thetaMin) * (thetaMax - thetaMin) * Vector3f(pHit.x, pHit.y, pHit.z); // Compute coefficients for fundamental forms Float E = Dot(dpdu, dpdu); Float F = Dot(dpdu, dpdv); Float G = Dot(dpdv, dpdv); Vector3f N = Normalize(Cross(dpdu, dpdv)); Float e = Dot(N, d2Pduu); Float f = Dot(N, d2Pduv); Float g = Dot(N, d2Pdvv); // Compute $\dndu$ and $\dndv$ from fundamental form coefficients Float invEGF2 = 1 / (E * G - F * F); Normal3f dndu = Normal3f((f * F - e * G) * invEGF2 * dpdu + (e * F - f * E) * invEGF2 * dpdv); Normal3f dndv = Normal3f((g * F - f * G) * invEGF2 * dpdu + (f * F - g * E) * invEGF2 * dpdv); // Compute error bounds for sphere intersection Vector3f pError = gamma(5) * Abs((Vector3f)pHit); // Initialize _SurfaceInteraction_ from parametric information *isect = (*ObjectToWorld)(SurfaceInteraction(pHit, pError, Point2f(u, v), -ray.d, dpdu, dpdv, dndu, dndv, ray.time, this)); // Update _tHit_ for quadric intersection *tHit = (Float)tShapeHit; return true; }
bool Triangle::Intersect(const Ray &ray, Float *tHit, SurfaceInteraction *isect, bool testAlphaTexture) const { ProfilePhase p(Prof::TriIntersect); ++nTests; // Get triangle vertices in _p0_, _p1_, and _p2_ const Point3f &p0 = mesh->p[v[0]]; const Point3f &p1 = mesh->p[v[1]]; const Point3f &p2 = mesh->p[v[2]]; // Perform ray--triangle intersection test // Transform triangle vertices to ray coordinate space // Translate vertices based on ray origin Point3f p0t = p0 - Vector3f(ray.o); Point3f p1t = p1 - Vector3f(ray.o); Point3f p2t = p2 - Vector3f(ray.o); // Permute components of triangle vertices and ray direction int kz = MaxDimension(Abs(ray.d)); int kx = kz + 1; if (kx == 3) kx = 0; int ky = kx + 1; if (ky == 3) ky = 0; Vector3f d = Permute(ray.d, kx, ky, kz); p0t = Permute(p0t, kx, ky, kz); p1t = Permute(p1t, kx, ky, kz); p2t = Permute(p2t, kx, ky, kz); // Apply shear transformation to translated vertex positions Float Sx = -d.x / d.z; Float Sy = -d.y / d.z; Float Sz = 1.f / d.z; p0t.x += Sx * p0t.z; p0t.y += Sy * p0t.z; p1t.x += Sx * p1t.z; p1t.y += Sy * p1t.z; p2t.x += Sx * p2t.z; p2t.y += Sy * p2t.z; // Compute edge function coefficients _e0_, _e1_, and _e2_ Float e0 = p1t.x * p2t.y - p1t.y * p2t.x; Float e1 = p2t.x * p0t.y - p2t.y * p0t.x; Float e2 = p0t.x * p1t.y - p0t.y * p1t.x; // Fall back to double precision test at triangle edges if (sizeof(Float) == sizeof(float) && (e0 == 0.0f || e1 == 0.0f || e2 == 0.0f)) { double p2txp1ty = (double)p2t.x * (double)p1t.y; double p2typ1tx = (double)p2t.y * (double)p1t.x; e0 = (float)(p2typ1tx - p2txp1ty); double p0txp2ty = (double)p0t.x * (double)p2t.y; double p0typ2tx = (double)p0t.y * (double)p2t.x; e1 = (float)(p0typ2tx - p0txp2ty); double p1txp0ty = (double)p1t.x * (double)p0t.y; double p1typ0tx = (double)p1t.y * (double)p0t.x; e2 = (float)(p1typ0tx - p1txp0ty); } // Perform triangle edge and determinant tests if ((e0 < 0 || e1 < 0 || e2 < 0) && (e0 > 0 || e1 > 0 || e2 > 0)) return false; Float det = e0 + e1 + e2; if (det == 0) return false; // Compute scaled hit distance to triangle and test against ray $t$ range p0t.z *= Sz; p1t.z *= Sz; p2t.z *= Sz; Float tScaled = e0 * p0t.z + e1 * p1t.z + e2 * p2t.z; if (det < 0 && (tScaled >= 0 || tScaled < ray.tMax * det)) return false; else if (det > 0 && (tScaled <= 0 || tScaled > ray.tMax * det)) return false; // Compute barycentric coordinates and $t$ value for triangle intersection Float invDet = 1 / det; Float b0 = e0 * invDet; Float b1 = e1 * invDet; Float b2 = e2 * invDet; Float t = tScaled * invDet; // Ensure that computed triangle $t$ is conservatively greater than zero // Compute $\delta_z$ term for triangle $t$ error bounds Float maxZt = MaxComponent(Abs(Vector3f(p0t.z, p1t.z, p2t.z))); Float deltaZ = gamma(3) * maxZt; // Compute $\delta_x$ and $\delta_y$ terms for triangle $t$ error bounds Float maxXt = MaxComponent(Abs(Vector3f(p0t.x, p1t.x, p2t.x))); Float maxYt = MaxComponent(Abs(Vector3f(p0t.y, p1t.y, p2t.y))); Float deltaX = gamma(5) * (maxXt + maxZt); Float deltaY = gamma(5) * (maxYt + maxZt); // Compute $\delta_e$ term for triangle $t$ error bounds Float deltaE = 2 * (gamma(2) * maxXt * maxYt + deltaY * maxXt + deltaX * maxYt); // Compute $\delta_t$ term for triangle $t$ error bounds and check _t_ Float maxE = MaxComponent(Abs(Vector3f(e0, e1, e2))); Float deltaT = 3 * (gamma(3) * maxE * maxZt + deltaE * maxZt + deltaZ * maxE) * std::abs(invDet); if (t <= deltaT) return false; // Compute triangle partial derivatives Vector3f dpdu, dpdv; Point2f uv[3]; GetUVs(uv); // Compute deltas for triangle partial derivatives Vector2f duv02 = uv[0] - uv[2], duv12 = uv[1] - uv[2]; Vector3f dp02 = p0 - p2, dp12 = p1 - p2; Float determinant = duv02[0] * duv12[1] - duv02[1] * duv12[0]; if (determinant == 0) { // Handle zero determinant for triangle partial derivative matrix CoordinateSystem(Normalize(Cross(p2 - p0, p1 - p0)), &dpdu, &dpdv); } else { Float invdet = 1 / determinant; dpdu = (duv12[1] * dp02 - duv02[1] * dp12) * invdet; dpdv = (-duv12[0] * dp02 + duv02[0] * dp12) * invdet; } // Compute error bounds for triangle intersection Float xAbsSum = (std::abs(b0 * p0.x) + std::abs(b1 * p1.x) + std::abs(b2 * p2.x)); Float yAbsSum = (std::abs(b0 * p0.y) + std::abs(b1 * p1.y) + std::abs(b2 * p2.y)); Float zAbsSum = (std::abs(b0 * p0.z) + std::abs(b1 * p1.z) + std::abs(b2 * p2.z)); Vector3f pError = gamma(7) * Vector3f(xAbsSum, yAbsSum, zAbsSum); // Interpolate $(u,v)$ parametric coordinates and hit point Point3f pHit = b0 * p0 + b1 * p1 + b2 * p2; Point2f uvHit = b0 * uv[0] + b1 * uv[1] + b2 * uv[2]; // Test intersection against alpha texture, if present if (testAlphaTexture && mesh->alphaMask) { SurfaceInteraction isectLocal(pHit, Vector3f(0, 0, 0), uvHit, -ray.d, dpdu, dpdv, Normal3f(0, 0, 0), Normal3f(0, 0, 0), ray.time, this); if (mesh->alphaMask->Evaluate(isectLocal) == 0) return false; } // Fill in _SurfaceInteraction_ from triangle hit *isect = SurfaceInteraction(pHit, pError, uvHit, -ray.d, dpdu, dpdv, Normal3f(0, 0, 0), Normal3f(0, 0, 0), ray.time, this); // Override surface normal in _isect_ for triangle isect->n = isect->shading.n = Normal3f(Normalize(Cross(dp02, dp12))); if (mesh->n || mesh->s) { // Initialize _Triangle_ shading geometry // Compute shading normal _ns_ for triangle Normal3f ns; if (mesh->n) { ns = (b0 * mesh->n[v[0]] + b1 * mesh->n[v[1]] + b2 * mesh->n[v[2]]); if (ns.LengthSquared() > 0) ns = Normalize(ns); else ns = isect->n; } else ns = isect->n; // Compute shading tangent _ss_ for triangle Vector3f ss; if (mesh->s) { ss = (b0 * mesh->s[v[0]] + b1 * mesh->s[v[1]] + b2 * mesh->s[v[2]]); if (ss.LengthSquared() > 0) ss = Normalize(ss); else ss = Normalize(isect->dpdu); } else ss = Normalize(isect->dpdu); // Compute shading bitangent _ts_ for triangle and adjust _ss_ Vector3f ts = Cross(ss, ns); if (ts.LengthSquared() > 0.f) { ts = Normalize(ts); ss = Cross(ts, ns); } else CoordinateSystem((Vector3f)ns, &ss, &ts); // Compute $\dndu$ and $\dndv$ for triangle shading geometry Normal3f dndu, dndv; if (mesh->n) { // Compute deltas for triangle partial derivatives of normal Vector2f duv02 = uv[0] - uv[2]; Vector2f duv12 = uv[1] - uv[2]; Normal3f dn1 = mesh->n[v[0]] - mesh->n[v[2]]; Normal3f dn2 = mesh->n[v[1]] - mesh->n[v[2]]; Float determinant = duv02[0] * duv12[1] - duv02[1] * duv12[0]; if (determinant == 0) dndu = dndv = Normal3f(0, 0, 0); else { Float invDet = 1 / determinant; dndu = (duv12[1] * dn1 - duv02[1] * dn2) * invDet; dndv = (-duv12[0] * dn1 + duv02[0] * dn2) * invDet; } } else dndu = dndv = Normal3f(0, 0, 0); isect->SetShadingGeometry(ss, ts, dndu, dndv, true); } // Ensure correct orientation of the geometric normal if (mesh->n) isect->n = Faceforward(isect->n, isect->shading.n); else if (reverseOrientation ^ transformSwapsHandedness) isect->n = isect->shading.n = -isect->n; *tHit = t; ++nHits; return true; }