Луч против эллипсоида пересечения - PullRequest
0 голосов
/ 01 сентября 2018

Я пытаюсь реализовать пересечение лучей с эллипсоидами путем "сжатия" пространства и создания лучей и сфер:

  1. создание mat3 S с радиусом эллипсоида по диагонали

  2. слабый луч путем умножения начала и направления на обратное значение S

  3. пересекающийся луч со сферой радиуса 1.0 в локальном пространстве

  4. умножить HitPoint на S, чтобы расшатать его.

Вот луч против сферы:

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;

Вот хлюпающая часть:

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

Текущий код сферы лучей предназначен для положения в мире, я пытаюсь заставить его работать в начале координат, поэтому это не должно иметь значения. Луч против обычной сферы работает нормально, проблема в эллипсоиде. Я потратил много времени на это, и что-то где-то не так.

1 Ответ

0 голосов
/ 01 сентября 2018

Проблема:

Центр масштабирования имеет значение.

Решение:

Выполните масштабирование относительно центра эллипсоида.

... а не источник, как вы делаете сейчас. Это потому, что, хотя направление луча будет одинаковым (это просто вектор направления), относительное смещение между масштабированным источником и центром сферы будет другим:

  • Масштабирование относительно происхождения (текущий код):

    Источник S' = qI * S, центр C' = qI * C --- S' - C' = qI * (S - C)

  • Масштабирование относительно центра эллипсоида (правильная процедура):

    Источник S" = qI * (S - C), центр C" = C --- S" - C" = qI * (S - C) - C

Два смещения отличаются положением исходного эллипсоида; таким образом, ваш текущий луч, скорее всего, пропустит / даст ложные срабатывания.


Исправленный код:

// 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);
...