0

I am trying to implement ray vs ellipsoid intersection by "squishing" space and doing ray vs sphere:

  1. create mat3 S with ellipsoid radius at diagonal

  2. squish ray by multiplying start and direction by an inverse of S

  3. intersect ray with sphere of radius 1.0 in local space

  4. multiply hitPoint by S to unsquish it.

Here is ray vs sphere:

float P = glm::dot(dir, sphereCenter-start);
float L = glm::distance(start, sphereCenter);
float d = sqrt(L*L - P*P);
if (d < radius) {
    float x0 = sqrt(1.f - d*d);
    hitPoint = start + dir*(P - x0);
    hitNormal = glm::normalize(hitPoint - sphereCenter);
}
else if (d == radius) {
    hitPoint = start + dir*P;
    hitNormal = glm::normalize(hitPoint - sphereCenter);
}
else {
    return false;
}
if (glm::distance(start, hitPoint) > dist) return false;
return true;

Here is the squishing part:

glm::vec3 S = start;
    glm::vec3 Dir = dir;

    auto sphereCenter = thisEntity()->transform()->getPosition();
    auto scale = thisEntity()->transform()->getScale();

    glm::mat3 q = glm::mat3(0);
    float x = _radius.x * scale.x;
    float y = _radius.y * scale.y;
    float z = _radius.z * scale.z;
    q[0][0] = x;
    q[1][1] = y;
    q[2][2] = z;
    glm::mat3 qI = glm::inverse(q);

    S = qI * S;
    Dir = qI * Dir;

    //calculate hit point in world space squished
    glm::vec3 hitPoint, hitNormal;
    if (!IntersectionsMath::instance()->segmentVsSphere(sphereCenter, S, Dir, dist, 1.f, hitPoint, hitNormal)) return;

    hitPoint = q * hitPoint;

    hit.pushHit(hitPoint, hitNormal, this);

Current ray sphere code is for world position, i'm trying to make it work at the origin so it shouldn't matter. Ray vs regular sphere works fine, ellipsoid is the problem. I spent a lot of time on this and something somewhere is wrong.

some rand
  • 33
  • 6
  • this [ray and ellipsoid intersection accuracy improvement](https://stackoverflow.com/q/25470493/2521214) might help a bit – Spektre Sep 03 '18 at 07:22

1 Answers1

2

Problem:

The center of scaling matters.

Solution:

Perform the scaling about the center of the ellipsoid.

... and not the origin as you are doing right now. This is because, although the direction of the ray will be the same (it is just a directional vector), the relative displacement between the scaled source and center of the sphere will be different:

  • Scaling about origin (current code):

    Source S' = qI * S, center C' = qI * C --- S' - C' = qI * (S - C)

  • Scaling about ellipsoid center (correct procedure):

    Source S" = qI * (S - C), center C" = C --- S" - C" = qI * (S - C) - C

The two displacements differ by the position of the original ellipsoid; thus your current ray will likely miss / give false positives.


Corrected code:

// scale about the ellipsoid's position by subtracting before multiplying
// more appropriate name would be "ellipseCenter" to avoid confusion
S_ = qI * (S - sphereCenter);

// this ::normalize should really be in the intersection function
Dir_ = glm::normalize(qI * Dir); 

// calculate hit point in world space squished
// ... but around the origin in the squashed coordinate system
glm::vec3 hitPoint, hitNormal;
if (!IntersectionsMath::instance()->segmentVsSphere(
          glm::vec3::ZERO, S_, Dir_,
          dist, 1.f,
          hitPoint, hitNormal)) return;

// re-apply the offset
hitPoint = q * hitPoint + sphereCenter

// problem: hitNormal will not be correct for the ellipsoid when scaled
// solution: divide through each component by square of respective semi-axis
// (will provide proof upon request)
hitNormal.x /= (x * x); hitNormal.y /= (y * y); hitNormal.z /= (z * z);
meowgoesthedog
  • 14,670
  • 4
  • 27
  • 40
  • Also Dir vector wasn't normalized. Got it to work, thanks. Dir = glm::normalize(qI * Dir); – some rand Sep 01 '18 at 19:46
  • @somerand oh and one more thing, scaling the normal returned by `segmentVsSphere` will *not* give the correct normal of the ellipse. (You may or may not notice later on.) Would you like me to include this in the answer? – meowgoesthedog Sep 01 '18 at 19:51
  • I fix it, but someone else might find it useful. – some rand Sep 01 '18 at 19:59