0
votes

I've been working on cobbling together a ray tracer. You know, for fun. So far most things are going as planned, but as soon as I started transforming my test spheres, it all went awry.

The fundamental concept is using one of standard shapes as origin, transforming the camera rays into object space, and then intersecting.

As long as the sphere is identical in object space and world space, it works as expected, but as soon as the spheres are scaled, normals and intersection points go wild.

I've been wracking my brains, and poring over this code over and over, but I just can't find the mistake. Fresh eyes would be much appreciated.

@implementation RTSphere

- (CGFloat)intersectsRay:(RTRay *)worldRay atPoint:(RTVector *)intersection normal:(RTVector *)normal material:(RTMaterial **)material {
  RTRay *objectRay = [worldRay rayByTransformingByMatrix:self.inverseTransformation];

  RTVector D = objectRay.direction;
  RTVector O = objectRay.start;

  CGFloat A, B, C;

  A = RTVectorDotProduct(D, D);
  B = 2 * RTVectorDotProduct(D,O);
  C = RTVectorDotProduct(O, O) - 0.25;

  CGFloat BB4AC = B * B - 4 * A * C;

  if (BB4AC < 0.0) {
    return -1.0;
  }

  CGFloat t0 = (-B - sqrt(BB4AC)) / 2 * A;
  CGFloat t1 = (-B + sqrt(BB4AC)) / 2 * A;

  if (t0 > t1) {
    CGFloat tmp = t0;
    t0 = t1;
    t1 = tmp;
  }

  if (t1 < 0.0) {
    return -1.0;
  }

  CGFloat t;

  if (t0 < 0.0) {
    t = t1;
  } else {
    t = t0;
  }

  if (material) {
    *material = self.material;
  }

  if (intersection) {
    RTVector isect_o = RTVectorAddition(objectRay.start, RTVectorMultiply(objectRay.direction, t));

    *intersection = RTVectorMatrixMultiply(isect_o, self.transformation);

    if (normal) {
      RTVector normal_o = RTVectorSubtraction(isect_o, RTMakeVector(0.0, 0.0, 0.0));
      RTVector normal_w = RTVectorUnit(RTVectorMatrixMultiply(normal_o, self.transformationForNormal));

      *normal = normal_w;
    }
  }

  return t;
}

@end

Why are the normals and intersection points not translating into world space as expected?

Edit: I'm moderately confident that my vector and matrix functions are mathematically sound; and I'm thinking it's chiefly a method error, but I recognize that I could be wrong.

1

1 Answers

1
votes

There is a lot of RT* code here "behind the scenes" that we have no way to know is correct, so I would start by making sure you have good unit tests of those math functions. The ones I would most suspect, from my experience managing transforms, is rayByTransformingByMatrix: or the value of inverseTransformation. I've found that this is very easy to get wrong when you combine transformations. Rotating and scaling is not the same as scaling and rotating.

At what point does it go wrong for you? Are you sure objectRay itself is correct? (If it isn't, then the rest of this function doesn't matter.) Again, unit test is your friend. You should hand-calculate several situations and then write unit tests to ensure that your methods return the right answers.