Учитывая ограничивающий прямоугольник и линию (две точки), определите, пересекает ли линия прямоугольник - PullRequest
6 голосов
/ 13 июля 2010

Учитывая ограничивающий прямоугольник, с такими определениями, как bounds.min.(x/y/z), bounds.max.(x/y/z) и двумя точками в трехмерном пространстве (выраженные как Vector3 объекты), как я могу определить, пересекает ли линия, образованная двумя точками, ограничивающий прямоугольник

Ответы [ 5 ]

10 голосов
/ 13 июля 2010

Существует реализация на C ++, доступная онлайн здесь: Пересечение линейного блока (http://www.3dkingdoms.com/weekly/weekly.php?a=3)

Другая ссылка со ссылками (и кодом) для множества тестов на пересечение: http://www.realtimerendering.com/intersections.html

Если вы хотите узнать больше о тестах пересечений, то это Библия: Обнаружение столкновений в реальном времени (Amazon)

РЕДАКТИРОВАТЬ: алгоритм в этой работе («Эффективный и надежный алгоритм пересечения Ray-Box», Эми Уильямс и Стив Баррус и Р. Кит Морли и Питер Ширли; графический журнал, GPU и game tools, Vol. 10 (1), 49-54, 2005) выглядит особенно лаконично и также поставляется с исходным кодом (C ++).

6 голосов
/ 13 июля 2010

Вот один способ сделать это, если вы хотите сделать математику самостоятельно: Пересечь линию с каждой из 6 плоскостей, созданных ограничительной рамкой.

Векторное представление линии: X = B + t * D, где B - это кортеж (x, y, z) базовой точки (скажем, вашей первой точки), а D - направление линии, снова выражается в виде кортежа (dx, dy, dz). Вы получаете направление, вычитая одну из точек из другой, поэтому, если у вас есть точки P1 (x1, y1, z1) и P2 (x2, y2, z2), то D = P2 - P1 и B = P1, что означает D = (x2 - x1, y2-y1, z2-z1). Мы назовем элементы этого вектора dx, dy и dz.

Параметрическое представление плоскости: x + y + z = c. Итак, преобразуйте вашу ограничивающую рамку в это представление, а затем используйте параметрическое представление вашей линии, например, три уравнения x = x1 + t dx, y = y1 + t dy, z = y1 + t * dz, чтобы заменить x, y и z в вашем уравнении плоскости. Решить за т. Поскольку каждая из ваших 6 плоскостей будет параллельна плоскости, созданной двумя осями, ваша задача станет проще; например, для плоскости, параллельной плоскости, созданной осями x и y, уравнение плоскости просто становится z = c, тогда как c является координатой z одной из точек вашей ограничительной рамки и так далее.

Теперь используйте t для вычисления точки пересечения линии с вашей плоскостью. (Если t <0 или> 1, то ваша линия пересекает ВНЕ P1-P2, если t> = 0 и t <= 1, тогда ваша линия пересекает плоскость где-то между P1 и P2) </p>

Теперь вы еще не закончили. Уравнение плоскости дает вам плоскость, а не прямоугольник, поэтому точка пересечения с плоскостью может фактически быть за пределами вашего прямоугольника, но поскольку теперь у вас есть координаты пересечения (x = x1 + t * dx и т. Д.), Вы можете легко увидеть, находится ли эта точка внутри прямоугольника вашей ограничительной рамки. Ваша задача теперь сводится к тому, чтобы проверить, находится ли точка в 2D-пространстве внутри ограничивающего прямоугольника, что тривиально проверить.

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

Могу поспорить, есть более быстрые способы сделать это, но это сработает.

5 голосов
/ 13 июля 2010

Вот код, который, кажется, работает, преобразованный из ответа Грега С. в C #:

bool CheckLineBox(Vector3 B1, Vector3 B2, Vector3 L1, Vector3 L2, ref Vector3 Hit)
{
    if (L2.x < B1.x && L1.x < B1.x) return false;
    if (L2.x > B2.x && L1.x > B2.x) return false;
    if (L2.y < B1.y && L1.y < B1.y) return false;
    if (L2.y > B2.y && L1.y > B2.y) return false;
    if (L2.z < B1.z && L1.z < B1.z) return false;
    if (L2.z > B2.z && L1.z > B2.z) return false;
    if (L1.x > B1.x && L1.x < B2.x &&
        L1.y > B1.y && L1.y < B2.y &&
        L1.z > B1.z && L1.z < B2.z)
    {
        Hit = L1;
        return true;
    }
    if ((GetIntersection(L1.x - B1.x, L2.x - B1.x, L1, L2, ref Hit) && InBox(Hit, B1, B2, 1))
      || (GetIntersection(L1.y - B1.y, L2.y - B1.y, L1, L2, ref Hit) && InBox(Hit, B1, B2, 2))
      || (GetIntersection(L1.z - B1.z, L2.z - B1.z, L1, L2, ref Hit) && InBox(Hit, B1, B2, 3))
      || (GetIntersection(L1.x - B2.x, L2.x - B2.x, L1, L2, ref Hit) && InBox(Hit, B1, B2, 1))
      || (GetIntersection(L1.y - B2.y, L2.y - B2.y, L1, L2, ref Hit) && InBox(Hit, B1, B2, 2))
      || (GetIntersection(L1.z - B2.z, L2.z - B2.z, L1, L2, ref Hit) && InBox(Hit, B1, B2, 3)))
        return true;

    return false;
}

bool GetIntersection(float fDst1, float fDst2, Vector3 P1, Vector3 P2, ref Vector3 Hit)
{
    if ((fDst1 * fDst2) >= 0.0f) return false;
    if (fDst1 == fDst2) return false;
    Hit = P1 + (P2 - P1) * (-fDst1 / (fDst2 - fDst1));
    return true;
}

bool InBox(Vector3 Hit, Vector3 B1, Vector3 B2, int Axis)
{
    if (Axis == 1 && Hit.z > B1.z && Hit.z < B2.z && Hit.y > B1.y && Hit.y < B2.y) return true;
    if (Axis == 2 && Hit.z > B1.z && Hit.z < B2.z && Hit.x > B1.x && Hit.x < B2.x) return true;
    if (Axis == 3 && Hit.x > B1.x && Hit.x < B2.x && Hit.y > B1.y && Hit.y < B2.y) return true;
    return false;
}
2 голосов
/ 11 февраля 2013

версия JavaScript, основанная на ответе SpikeX и glMatrix:

// all args are Vec3, Hit will be filled by this algo
function checkLineBox( B1, B2, L1, L2, Hit)
{
    if (L2[0] < B1[0] && L1[0] < B1[0]) return false;
    if (L2[0] > B2[0] && L1[0] > B2[0]) return false;
    if (L2[1] < B1[1] && L1[1] < B1[1]) return false;
    if (L2[1] > B2[1] && L1[1] > B2[1]) return false;
    if (L2[2] < B1[2] && L1[2] < B1[2]) return false;
    if (L2[2] > B2[2] && L1[2] > B2[2]) return false;
    if (L1[0] > B1[0] && L1[0] < B2[0] &&
        L1[1] > B1[1] && L1[1] < B2[1] &&
        L1[2] > B1[2] && L1[2] < B2[2])
    {
        vec3.set( L1, Hit);
        return true;
    }

    if ((getIntersection(L1[0] - B1[0], L2[0] - B1[0], L1, L2, Hit) && inBox(Hit, B1, B2, 1))
      || (getIntersection(L1[1] - B1[1], L2[1] - B1[1], L1, L2, Hit) && inBox(Hit, B1, B2, 2))
      || (getIntersection(L1[2] - B1[2], L2[2] - B1[2], L1, L2, Hit) && inBox(Hit, B1, B2, 3))
      || (getIntersection(L1[0] - B2[0], L2[0] - B2[0], L1, L2, Hit) && inBox(Hit, B1, B2, 1))
      || (getIntersection(L1[1] - B2[1], L2[1] - B2[1], L1, L2, Hit) && inBox(Hit, B1, B2, 2))
      || (getIntersection(L1[2] - B2[2], L2[2] - B2[2], L1, L2, Hit) && inBox(Hit, B1, B2, 3)))
        return true;

    return false;
}

var temp = vec3.create();
function getIntersection( fDst1, fDst2, P1, P2, Hit)
{
    if ((fDst1 * fDst2) >= 0) return false;
    if (fDst1 == fDst2) return false;

    vec3.subtract(P2, P1, temp);
    vec3.scale( temp, (-fDst1 / (fDst2 - fDst1)));
    vec3.add( temp, P1, Hit);

    return true;
}

function inBox(Hit, B1, B2, Axis)
{
    if (Axis == 1 && Hit[2] > B1[2] && Hit[2] < B2[2] && Hit[1] > B1[1] && Hit[1] < B2[1]) return true;
    if (Axis == 2 && Hit[2] > B1[2] && Hit[2] < B2[2] && Hit[0] > B1[0] && Hit[0] < B2[0]) return true;
    if (Axis == 3 && Hit[0] > B1[0] && Hit[0] < B2[0] && Hit[1] > B1[1] && Hit[1] < B2[1]) return true;
    return false;
}
1 голос
/ 13 июля 2010

Вы можете представить свою ограничивающую рамку в виде 12 треугольников (2 для каждого из 6 граней). Затем вы можете проверить пересечение вашей линии с каждым из них. У меня есть функция пересечения линии-треугольника, но она была написана для моего собственного программного средства рендеринга, а не для D3D. Я могу попытаться преобразовать его, если вам нужен код.

...