Closed BrianSwift closed 4 years ago
This is a tough case numerically--quite far away spheres and a narrow fov. As it turns out, the underlying precision issue is nicely described in this Ray Tracing Gems chapter.
With the fix described there (diff of a hacked in version of it below), results are much better (but not perfect, on my system at least.) However, my inclination is to leave the github pbrt-v3 code as is, in the interests of minimizing divergence from the book text. This is already fixed in the eventually forthcoming pbrt-v4.
Thanks!
diff --git a/src/shapes/sphere.cpp b/src/shapes/sphere.cpp
index 40b819a..51d5ed9 100755
--- a/src/shapes/sphere.cpp
+++ b/src/shapes/sphere.cpp
@@ -66,7 +66,32 @@ bool Sphere::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect,
// Solve quadratic equation for _t_ values
EFloat t0, t1;
+#if 0
if (!Quadratic(a, b, c, &t0, &t1)) return false;
+#else
+ // RT Gems
+ EFloat rootDiscrim;
+ {
+ EFloat f = b / (2 * a); // (o . d) / LengthSquared(d)
+ EFloat fp[3] = { ox - f * dx, oy - f * dy, oz - f * dz };
+ EFloat sqrtf = sqrt(fp[0] * fp[0] + fp[1] * fp[1] + fp[2] * fp[2]);
+ EFloat discrim = 4 * a * ((EFloat(radius)) - sqrtf) *
+ ((EFloat(radius)) + sqrtf);
+
+ if (discrim.LowerBound() < 0) return false;
+ rootDiscrim = sqrt(discrim);
+ }
+
+ // Compute quadratic _t_ values
+ EFloat q;
+ if ((Float)b < 0)
+ q = -.5 * (b - rootDiscrim);
+ else
+ q = -.5 * (b + rootDiscrim);
+ t0 = q / a;
+ t1 = c / q;
+ if (t0.LowerBound() > t1.LowerBound()) std::swap(t0, t1);
+#endif
// Check quadric shape _t0_ and _t1_ for nearest intersection
if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false;
@@ -174,7 +199,32 @@ bool Sphere::IntersectP(const Ray &r, bool testAlphaTexture) const {
// Solve quadratic equation for _t_ values
EFloat t0, t1;
+#if 0
if (!Quadratic(a, b, c, &t0, &t1)) return false;
+#else
+ // RT Gems
+ EFloat rootDiscrim;
+ {
+ EFloat f = b / (2 * a); // (o . d) / LengthSquared(d)
+ EFloat fp[3] = { ox - f * dx, oy - f * dy, oz - f * dz };
+ EFloat sqrtf = sqrt(fp[0] * fp[0] + fp[1] * fp[1] + fp[2] * fp[2]);
+ EFloat discrim = 4 * a * ((EFloat(radius)) - sqrtf) *
+ ((EFloat(radius)) + sqrtf);
+
+ if (discrim.LowerBound() < 0) return false;
+ rootDiscrim = sqrt(discrim);
+ }
+
+ // Compute quadratic _t_ values
+ EFloat q;
+ if ((Float)b < 0)
+ q = -.5 * (b - rootDiscrim);
+ else
+ q = -.5 * (b + rootDiscrim);
+ t0 = q / a;
+ t1 = c / q;
+ if (t0.LowerBound() > t1.LowerBound()) std::swap(t0, t1);
+#endif
// Check quadric shape _t0_ and _t1_ for nearest intersection
if (t0.UpperBound() > ray.tMax || t1.LowerBound() <= 0) return false;
Tried the above patch with a 32-bit float pbrt, and only difference appears to be a black pixel in the middle of image.