当相机远离环面时,射线和环面方程相交的数值错误

Numerical bug in intersecting the equation of ray and torus when the camera is far from the torus

我试图在不对环面进行三角剖分的情况下对环面进行光线追踪,而只是通过将光线和环面解析方程相交。我用以下代码做到了这一点:

void circularTorusIntersectFunc(const CircularTorus* circularToruses, RTCRay& ray, size_t item)
{
  const CircularTorus& torus = circularToruses[item];

  Vec3fa O = ray.org /*- sphere.p*/;
  Vec3fa Dir = ray.dir;
  O.w = 1.0f;
  Dir.w = 0.0f;
  O = torus.inv_transform.mult(O);
  Dir = torus.inv_transform.mult(Dir);

  // r1: cross section of torus
  // r2: the ring's radius
  //  _____                     ____
  // / r1  \------->r2<--------/    \
  // \_____/                   \____/

  float r2 = sqr(torus.r1);
  float R2 = sqr(torus.r2);

  double a4 = sqr(dot(Dir, Dir));
  double a3 = 4 * dot(Dir, Dir) * dot(O, Dir);
  double a2 = 4 * sqr(dot(O, Dir)) + 2 * dot(Dir, Dir) * (dot(O, O) - r2 - R2) + 4 * R2 * sqr(Dir.z);
  double a1 = 4 * dot(O, Dir) * (dot(O, O) - r2 - R2) + 8 * R2 * O.z * Dir.z;
  double a0 = sqr(dot(O, O) - r2 - R2) + 4 * R2 * sqr(O.z) - 4 * R2 * r2;

  a3 /= a4; a2 /= a4; a1 /= a4; a0 /= a4;

  double roots[4];
  int n_real_roots;
  n_real_roots = SolveP4(roots, a3, a2, a1, a0);

  if (n_real_roots == 0) return;

  Vec3fa intersect_point;
  for (int i = 0; i < n_real_roots; i++)
  {
    float root = static_cast<float>(roots[i]);
    intersect_point = root * Dir + O;

    if ((ray.tnear <= root) && (root <= ray.tfar)) {

      ray.u = 0.0f;
      ray.v = 0.0f;
      ray.tfar = root;
      ray.geomID = torus.geomID;
      ray.primID = item;
      Vec3fa normal(
        4.0 * intersect_point.x * (sqr(intersect_point.x) + sqr(intersect_point.y) + sqr(intersect_point.z) - r2 - R2),
        4.0 * intersect_point.y * (sqr(intersect_point.x) + sqr(intersect_point.y) + sqr(intersect_point.z) - r2 - R2),
        4.0 * intersect_point.z * (sqr(intersect_point.x) + sqr(intersect_point.y) + sqr(intersect_point.z) - r2 - R2) + 8 * R2*intersect_point.z,
        0.0f
        );

      ray.Ng = normalize(torus.transform.mult(normal));
    }
  }
}

求解SolveP4函数方程的代码取自Solution of cubic and quatric functions

问题是当我们仔细观察环面时,它工作得很好,如下所示:

但是当我缩小相机时,相机正在看着远离它的环面,它突然变得如此嘈杂并且形状无法很好地识别。我尝试每个像素使用超过 1 个样本,但我仍然遇到同样的问题。具体如下:

看来我遇到了一个数值问题,但我不知道如何解决。任何人都可以帮助我吗?

此外,值得一提的是,我正在使用英特尔的 Embree Lib 对环面进行光线追踪。

更新(单色):

我认为很多问题是使用单精度浮点数而不是双精度。

定义两个函数

double dsqr(double x) { return x*x; }

double ddot(const Vec3fa &a,Vec3fa &b) {
  double x1 = a.x, y1 = a.y, z1 = a.z;
  double x2 = b.x, y2 = b.y, z2 = b.z;
  return x1*x2 + y1*y2 + z1*z2;
}

求平方和点积,但使用双精度。更改 r2 R2 a4 a3 a2 a1 和 a0 的计算以使用这些

double r2 = dsqr(torus.r1);
double R2 = dsqr(torus.r2);

double a4 = dsqr(ddot(Dir, Dir));
double a3 = 4 * ddot(Dir, Dir) * ddot(O, Dir);
double a2 = 4 * dsqr(ddot(O, Dir)) + 2 * ddot(Dir, Dir) * (ddot(O, O) - r2 - R2)
    + 4 * R2 * dsqr(Dir.z);
double a1 = 4 * ddot(O, Dir) * (ddot(O, O) - r2 - R2) + 8 * R2 * O.z * Dir.z;
double a0 = dsqr(ddot(O, O) - r2 - R2) + 4 * R2 * dsqr(O.z) - 4 * R2 * r2;

其余代码全部相同。在我的测试中,这使得看起来模糊的图像看起来非常清晰。

当我写我的 raytracer 时(顺便说一句,我使用了一本名为 "Ray Tracing from the Ground Up" 的好书)我也遇到了 Toruses 的一些问题。 那时我使用来自 Graphics Gems github repo 的算法来计算射线环相交点。解决方案是简单地使用较小的圆环,例如当我的环面的外半径大于 100.0 并且光线开始于 (0,0,0) 时,我的光线追踪器出现了大量的数值错误。 使用像 1.0 这样更小的环面半径解决了我的问题。

这些数值错误的根源在于环面多项式的构建系数,环面的大小为 100.0,在计算过程中生成的一些系数可能会超过 1e20double 保证大约 15 位有效数字的精度,造成了很大的精度损失。

PovRay 对此提供了有趣且有效的解决方案。 只需将射线的原点移动到非常靠近环面的位置,多项式求解器的系数就会有很好的值。 接近程度:原点应位于半径为 mayor+2*minor 的球体上。 ...并按照@csharpfolk

的建议将市长半径保持为 1