4

I am trying to ray trace a torus without triangulating the torus and just by intersecting the ray and torus analytic equation. I did that with the following code:

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));
    }
  }
}

The code to solve the equation for SolveP4 function is taken from Solution of cubic and quatric functions.

The problem is when we are looking at the torus closely, it works pretty nice as follows:

Ray Tracing a Torus from near

But when I zoom out the camera, so camera is looking at the torus far from it, it suddenly gets so noisy and it is shape is not well identified. I tried to use more than 1 samples per pixels but still I have the same problem. It is as follows:

Unclear Torus from far when it is ray traced

It seems I am facing a numerical problem but I dont know how to solve it. Anyone can help me with that?

Also, it is good to mention that I am raytracing the torus with Intel's Embree Lib.

Update (Single Color):

Single Color Correct ray traced torus Single Color incorrect ray traced torus Very far Single color incorrect ray traced torus

Salix alba
  • 7,536
  • 2
  • 32
  • 38
mmostajab
  • 1,937
  • 1
  • 16
  • 37
  • That second image doesn't look so bad to me. Do you have a specific example, for which that function gives the wrong result? – Beta Jun 28 '15 at 15:48
  • @Beta No, I don't have any specific example but trust me that the quality is not good and if you look at it in big resolution, you cannot identify it is a torus. – mmostajab Jun 28 '15 at 16:46
  • 1
    I have similar problem with GLSL [ray and ellipsoid intersection accuracy improvement](http://stackoverflow.com/q/25470493/2521214) read it and check if things from it could help you. By zooming you change the scale or panning camera to larger distance from object? What are the distances/sizes/scales for both images? – Spektre Jun 29 '15 at 06:34
  • Could you try painting it in just a solid colour. That way we could tell if the problem is with the intersection or with the calculation of reflected rays/ lighting model. – Salix alba Jun 29 '15 at 14:00
  • @Salixalba single color screenshots are added. – mmostajab Jun 29 '15 at 14:05
  • Are you getting your equation from http://www.wseas.org/multimedia/journals/computers/2013/025705-201.pdf – Salix alba Jun 29 '15 at 14:28
  • @Salixalba No, I got the equation from: http://www.emeyex.com/site/projects/raytorus.pdf – mmostajab Jun 29 '15 at 14:29
  • Is the wseas paper better? – mmostajab Jun 29 '15 at 14:30
  • They should give identical equations. It might be worth using wseas form to see if it makes a difference. There is a chance there might be a sign error somewhere. – Salix alba Jun 29 '15 at 14:46
  • @Salixalba but the equation is completely correct and giving me the correct result. Just when the viewer is far from the view point, the coefficients gets big and then the numerical problems happen. I somehow figured it out. So, when the coefficient are big, I simply calculate the `t/100` and multiply the coefficients by `pow(1/100, n)`, so the equation changes to `a4*pow(1/100, 4)*t^4+a3*pow(1/100, 3)*t^3+a2*pow(1/100, 2)*t^2+a1*pow(1/100, 1)*t+a0`. This equation has more exact answers :) – mmostajab Jun 29 '15 at 14:50
  • @Salixalba multiplying `0.01` by the `t` did not help me. It is not a good way. – mmostajab Jun 29 '15 at 15:15

3 Answers3

1

I think a lot of the problem is using single precision float rather than double precision.

Define two functions

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;
}

to find the square and the dot product but using double precision. Change the calculations of r2 R2 a4 a3 a2 a1 and a0 to use these

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;

all the remaining code is the same. In my test this made a fuzzy looking image look perfectly sharp.

Salix alba
  • 7,536
  • 2
  • 32
  • 38
  • It decreases the noise a lot. Also, I added this code chunk to intersect part of the code, so when the ray is intersected I will choose the intersection point with the minimum value of residual: `double new_s = a4 * pow(root, 4) + a3 * pow(root, 3) + a2 * pow(root, 2) + a1 * root + a0; if (std::abs(new_s) < s) s = std::abs(new_s);` Then it becomes really clear :) – mmostajab Jun 30 '15 at 07:15
1

When I was writing my raytracer (BTW I was using a great book called "Ray Tracing from the Ground Up") I had some problems with Toruses too. Back then I used algorithms from Graphics Gems github repo to compute ray torus intersection points. The solution was simply to use smaller toruses e.g. when my torus had outer radius greater than 100.0 and the ray started at (0,0,0) my raytracer experienced plenty of numerical errors. Using smaller torus radii like 1.0 solved my issues.

The source of these numerical errors lies in building coefficient for torus polynomial, with torus of size 100.0 some coefficient that are generated during that computation can exceed 1e20. With double precision that guarantees about 15 significant digits, that created substantial loss of precision.

csharpfolk
  • 4,124
  • 25
  • 31
1

PovRay give interesting and efficient solution for this. Just move origin of ray very close to torus and coefficient will have good values for polynomial solver. How close: origin should be on sphere with radius mayor+2*minor. ... and keep mayor radius to one as suggested by @csharpfolk

DejanM
  • 81
  • 1
  • 4
  • If you want to make depressed equation without depressing equation then move ray origin for: minus( dot( origin, direction)). Assuming that length of direction is one. Then you will have monic and depressed polynomial. Ferrari and Descartes will like that. Do not forget to move roots back. – DejanM Oct 02 '19 at 13:52