3D отрезок линии и пересечение плоскости - PullRequest
4 голосов
/ 24 августа 2011

Я пытаюсь реализовать тест пересечения отрезка и плоскости, который будет возвращать значение true или false в зависимости от того, пересекает ли он плоскость или нет.Он также вернет точку контакта на плоскости, где линия пересекается, если линия не пересекается, функция все равно должна вернуть точку пересечения, если сегмент линии был лучом.Я использовал информацию и код из Обнаружения столкновений Кристера Эриксона в реальном времени, но я не думаю, что я реализую его правильно.

Используемая им плоскость получена из нормали и вершины треугольника.Мне нужно найти местоположение пересечения на плоскости, независимо от того, находится ли оно на треугольнике, который я использовал для получения плоскости.

enter image description here

ПараметрыВот следующие функции:

contact = the contact point on the plane, this is what i want calculated
ray = B - A, simply the line from A to B
rayOrigin = A, the origin of the line segement
normal = normal of the plane (normal of a triangle)
coord = a point on the plane (vertice of a triangle)

Вот код, который я использую:

bool linePlaneIntersection(Vector& contact, Vector ray, Vector rayOrigin, Vector normal, Vector coord) {

    // calculate plane
    float d = Dot(normal, coord);

    if (Dot(normal, ray)) {
        return false; // avoid divide by zero
    }

    // Compute the t value for the directed line ray intersecting the plane
    float t = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);

    // scale the ray by t
    Vector newRay = ray * t;

    // calc contact point
    contact = rayOrigin + newRay;

    if (t >= 0.0f && t <= 1.0f) {
        return true; // line intersects plane
    }
    return false; // line does not
}

В моих тестах он никогда не возвращает истину ... какие-либо идеи?

Ответы [ 3 ]

7 голосов
/ 14 февраля 2016

Я отвечаю на это, потому что он впервые появился в Google, когда его спросили о c ++ примере пересечения лучей:)

Код всегда возвращает false, потому что вы вводите здесь if:

if (Dot(normal, ray)) {
   return false; // avoid divide by zero
}

И скалярное произведение равно нулю только в том случае, если векторы перпендикулярны, и это тот случай, которого вы хотите избежать (без пересечений), и ненулевые числа истинны в C.Таким образом, решение состоит в том, чтобы отрицать (!) Или сделать Dot (...) == 0.Во всех остальных случаях будет пересечение.

На вычислении пересечения: Все точки X плоскости следуют уравнению

Точка (N, X) = d

Где N - нормаль, а d можно найти, поместив известную точку плоскости в уравнение.

float d = Dot(normal, coord);

На луче все точки s линии могут быть выражены в виде точки p и вектора, задающего направление D :

s = p + x * D

Так что если мы ищем, какой xs находится в плоскости, мы имеем

Точка (N, s) = d Точка (N, p + x * D) = d

Точечное произведение ab равно transpose (a) * b ,Пусть транспонировать (N) будет Nt .

Nt * (p + x * D) = d Nt * p + Nt * D * x = d (x скаляр) x = (d - Nt * p) / (Nt * D) x = (d - точка (N, p)) / точка (N, D)

Что дает нам:

<code>float x = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);
Теперь мы можем получить пересечениеукажите в уравнении строки x

s = p + x * D

<code>Vector intersection = rayOrigin + x*ray;
Вышеуказанный код обновлен:
<code>bool linePlaneIntersection(Vector& contact, Vector ray, Vector rayOrigin, 
                           Vector normal, Vector coord) {
    // get d value
    float d = Dot(normal, coord);<br/>
    if (Dot(normal, ray) == 0) {
        return false; // No intersection, the line is parallel to the plane
    }<br/>
    // Compute the X value for the directed line ray intersecting the plane
    float x = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);<br/>
    // output contact point
    *contact = rayOrigin + normalize(ray)*x; //Make sure your ray vector is normalized
    return true;
}
В сторону 1: Что означает значение d ? Для двух векторов a и b точечное произведение фактически возвращает длину ортогональной проекции одного вектора на другой, умноженный на этот другой вектор.Но если a нормализовано (длина = 1), Dot (a, b) - это длина проекции b на a .В случае нашей плоскости d дает нам направленное расстояние всех точек плоскости в нормальном направлении к началу координат ( a - нормаль).Затем мы можем определить, находится ли точка на этой плоскости, сравнивая длину проекции с нормалью (произведение точек). В сторону 2: Как проверить, пересекает ли луч треугольник?(Используется для трассировки лучей) Чтобы проверить, входит ли луч в треугольник, заданный 3-мя вершинами, сначала нужно сделать то, что показано здесь, получить пересечение с плоскостью, образованной треугольником.Следующий шаг - посмотреть, лежит ли эта точка в треугольнике.Этого можно достичь, используя барицентрические координаты, которые выражают точку на плоскости как комбинацию трех точек на ней.См. Барицентрические координаты и преобразование из декартовых координат .
2 голосов
/ 24 августа 2011

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

// calculate plane
float d = Dot(normal, coord);

Здесь ваше значение d соответствует произведению точки между нормалью плоскости (вектором) и точкой в ​​пространстве (точкой на плоскости).Это кажется неправильным.В частности, если у вас есть какая-либо плоскость, проходящая через начало координат и использующая начало координат в качестве координатной точки, вы в конечном итоге вычислите

d = Dot(normal, (0, 0, 0)) = 0

и сразу же вернете значение false.Я не уверен, что вы намеревались сделать здесь, но я уверен, что это не то, что вы имели в виду.

Еще одно место в коде, которое кажется подозрительным, это строка:

// Compute the t value for the directed line ray intersecting the plane
float t = (d - Dot(normal, rayOrigin)) / Dot(normal, ray);

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

Надеюсь, это поможет!

1 голос
/ 24 августа 2011

Это все выглядит хорошо для меня. Я независимо проверил алгебру, и это выглядит хорошо для меня.

В качестве примера теста:

A = (0,0,1)
B = (0,0,-1)
coord = (0,0,0)
normal = (0,0,1)

Это дает:

d = Dot( (0,0,1), (0,0,0)) = 0
Dot( (0,0,1), (0,0,-2)) = -2 // so trap for the line being in the plane passes.
t = (0 - Dot( (0,0,1), (0,0,1) ) / Dot( (0,0,1), (0,0,-2)) = ( 0 - 1) / -2 = 1/2
contact = (0,0,1) + 1/2 (0,0,-2) = (0,0,0) // as expected.

Таким образом, учитывая поправку после ответа @ templatetypedef, единственная область, в которой я вижу проблему, связана с реализацией одной из других операций, будь то Dot() или операторов Vector.

...