Обнаружение столкновений в 3D: выпуклая оболочка против выпуклой оболочки, требуется положение и нормаль

Я хочу знать приблизительное трехмерное положение и трехмерной нормали места столкновения двух трехмерных выпуклых корпусов (A против B).

ЦП в скобках показывает относительное время ЦП, необходимое для моей законченной программы.

Часть 1: ранний выход (ЦП 1%)

На первом этапе я использую очень дешевый алгоритм - теорему об оси разделения.
Для Например, я использую 15 осей для 2 кубов. (В реальных случаях формы более сложные.)
Если есть хотя бы одна ось, которая может разделяться, return "no-collide".
В противном случае выполните следующую часть.

Часть 2: вершина против объема (ЦП 10%)

  • Проверьте каждую вершину A - не находится ли она внутри B.
  • Проверьте каждую вершину B - не находится ли она внутри A.

введите описание изображения здесь

Часть 3: Edge против Edge (ЦП> 20%)

Странный случай например. https://gamedev.stackexchange.com/questions/75437/collision-detection-3d-rectangles-using-sat. Я украл изображение оттуда: -

введите описание изображения здесь

Таким образом, мне также нужно край против края.

  • Для каждой пары ребер A и B (12 * 12 = 144 пары) найдите ближайшую точку на ребре A относительно края B. Проверить, находится ли вершина внутри B.
  • (наоборот) Для каждой пары ребер B и A проверьте, находится ли такая вершина внутри A.

Вау, это много вычислений.
Однако это еще не конец.

Проблема

  1. Сообщаемое положение столкновения не очень точное (слева: текущее, справа: желаемое): -

    введите описание изображения здесь

    Чтобы решить эту проблему, я подумал о создании новой выпуклой формы = A intersect B.
    Есть несколько бесплатных библиотек C ++ (например, OpenMesh), но я думаю, что это слишком дорого для ЦП.
    Обратите внимание, что мне не нужно, чтобы он был точным.

  2. Он также иногда сообщает о неправильном нормальном состоянии (слева: текущее, справа: желание): -

    введите описание изображения здесь

    ^ Эту проблему можно решить, добавив проверку край (из A) против лица (из B), но это сделало бы обнаружение столкновений еще более дорогостоящим.

Вопрос

Похоже, что распространенные алгоритмы в Интернете (из которых я копирую) распознают только микрочипы.
ИМХО, алгоритм вершинного объема / ребра-ребра фокусируется на топологии, а не на том факте, что обе формы твердые объемы.

Какой алгоритм более точен (1-й приоритет) и, возможно, дешевле?
Мой подход может быть неправильным на базовом уровне.

Чтобы ускорить процесс, я уже немного подрезал, например выберите только пару ребер A и B, которые расположены близко друг к другу.

Ссылки: -

Изменить (через 10 дней)

Теперь я могу найти все пересекающиеся точки выпуклости (выпуклость изображена в виде розового треугольника / прямоугольника): - введите описание изображения здесь

Вот как я считаю нормальным.

Для каждой разделяющей оси (все = 15 осей) я проецирую розовую выпуклость на ось.
Ось, которая дает кратчайшее проекционное расстояние (розовая стрелка ) должно быть нормальным направлением.

Мое вышеупомянутое предположение часто является правильным (например, слева), но иногда неверным (например, правильным).
Как улучшить его с меньшими затратами на ЦП?


person javaLover    schedule 06.07.2019    source источник
comment
Это домашнее задание?   -  person Willem Van Onsem    schedule 06.07.2019
comment
@ Виллем Ван Онсем Ха-ха. Нет, это часть моего игрового движка ECS. Я достаточно сумасшедший, чтобы создать свой собственный Physics Engine (Bullet мне не подходит).   -  person javaLover    schedule 06.07.2019
comment
Я не понимаю, в чем проблема в вашем первом изображении «текущее и желаемое»? Почему изображение слева показывает неправильное изображение, а изображение справа показывает правильную точку столкновения ?. Что касается части 3. Если вы перейдете по ссылке, которую вы дали, ответ объясняет, что SAT достаточно для обнаружения столкновения, но вам нужно протестировать 15 осей, а не только 6 нормальных осей. gamedev.stackexchange.com/questions/44500/. Но вам нужно больше поработать, чтобы определить желаемую точку соприкосновения.   -  person unlut    schedule 08.07.2019
comment
@unlut Это не совсем правильно, потому что сообщаемая точка столкновения должна быть примерно в центре зоны перекрытия. IMHO, точка, которая находится рядом с границей перекрытия, не очень хороший представитель события столкновения. По поводу части 3, вы имеете в виду, что я могу расширить SAT по 15 осям, чтобы найти положение контакта?   -  person javaLover    schedule 08.07.2019
comment
Вы думали о точном времени и месте столкновения? Вам понадобится что-то вроде кинематических структур данных, чтобы отслеживать это, но это значительно упростит определение лица и точки, когда вы обнаружите столкновение. Вы также можете попробовать повернуть движение вспять и отследить исходную точку столкновения. Однако это изменило бы проблему.   -  person Ninetails    schedule 08.07.2019
comment
@Ninetails Спасибо. Я новичок в этом. Как называется техника / подход (чтобы я мог прочитать больше)?   -  person javaLover    schedule 09.07.2019
comment
Я имею в виду, что SAT с 15 осями достаточно, чтобы знать, произошло ли столкновение или нет, вам не нужно никакой специальной работы для обнаружения столкновений край-край. Если вы посмотрите исходный код пули, они проводят 15 тестов. Если столкновение является столкновением кромка-кромка (часть if (код ›6)), они вычисляют точки контакта легче по сравнению с столкновениями без кромки (остальная часть функции). Я не знаю подробностей того, что они делают, чтобы получить точки соприкосновения неграничных коллизий, но я уверен, что вы можете следить за этим, учитывая, что код хорошо прокомментирован.   -  person unlut    schedule 09.07.2019
comment
@unlut Круто, спасибо. Я не могу найти никаких ссылок / теорий по этому поводу (15 осей = больше не нужно обнаружение края-края), но, судя по вашему комментарию + различным диаграммам, теперь я чувствую / верю, что это правда. Спасибо, что подтвердили это. XD   -  person javaLover    schedule 09.07.2019
comment
Вы можете найти кинетические структуры данных. Вот ссылка на введение к одной из них: ссылка. Они используются для отслеживания вещей, структура которых со временем меняется (например, выпуклая оболочка набора движущихся точек или направление движения объектов, когда они могут столкнуться). Чтение о них должно дать вам некоторую приятную абстракцию, над которой можно подумать, когда вы создадите свой движок. Обычно сложнее решить проблему с генетическими структурами данных, но их решения могут иметь некоторые существенные преимущества (здесь вы получаете точное время).   -  person Ninetails    schedule 09.07.2019
comment
@Ninetails Ого, это новая вселенная для меня; вроде как продвинутый вид непрерывного обнаружения столкновений. Благодарить.   -  person javaLover    schedule 09.07.2019
comment
Если у вас произвольная выпуклая оболочка, тогда алгоритм GJK может быть удобно для обнаружения столкновений. Что касается расстояния проникновения, я думаю, что его точное вычисление вряд ли возможно, поэтому следует использовать эвристику.   -  person stgatilov    schedule 09.07.2019
comment
@stagtilov Спасибо. Это полезно. Лично я не очень ценю Simplex, потому что его сложно сделать (числовым) стабильным. Например. Код C ++ mosh в stackoverflow.com/questions/3956950/, но для меня это дает неверный результат теста (coliru.stacked-crooked.com / a / 5b750c415321058a).   -  person javaLover    schedule 10.07.2019


Ответы (2)


Игровые движки обычно моделируют во времени серию дискретных шагов. Как следствие, система столкновений может попасть в сложные (дорогостоящие в вычислительном отношении) случаи из-за взаимопроникновения (ваш случай) или когда вещи движутся быстро - туннелирование, когда A находится с одной стороны от B на шаге N и полностью с другой стороны от B. на шаге N + 1. Это еще труднее, если вам нужно иметь дело с многотельным контактом, непрерывным контактом, невыпуклым, шарнирным или мягким предметом. Ой! мы моделируем весь мир.

Вы хотите сделать "игровую физику" и использовать приближения, чтобы выкупить частоту кадров ... В конце концов, вы можете скрыть много ошибок с помощью кучи дыма или световых вспышек. :-)

Существует класс алгоритмов, которые явно учитывают смоделированное время, чтобы помочь системе столкновений. Есть много способов реализовать систему «непрерывного обнаружения столкновений». Вы можете начать здесь, но сначала вы должны внимательно прочитать, прежде чем переходить к написанию кода. К счастью, о столкновениях написано много. вот хорошее место для начала https://docs.unity3d.com/Manual/ContinuousCollisionDetection.html https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=20

Вот одна предлагаемая эвристика, которая может работать в вашей существующей системе ... Этот эвристический метод может работать в игре, подобной Astroids 3D, где объекты свободно плавают в пространстве. Этого может быть достаточно для того, что вам нужно.

Изображение каждого объекта хранит свой вектор текущего состояния (положение, ориентацию, скорость, ускорение, вращение ...) и его предыдущий вектор состояния с предыдущего временного шага.

Предположим, вы обнаружили потенциальное столкновение между объектами A и B в момент времени = текущий.

Для time = previous предположим, что A и B не контактируют.

Вычислите ближайшие точки на поверхностях A и B, соответственно, в time = prev, используя предыдущие векторы состояния A и B. (closestA, closestB).

Отрезок линии (closestA, closestB) будет иметь ненулевую длину в момент time = previous. Вы можете просто использовать closestB для своей позиции и normal, но это будет иметь некоторую ошибку, пропорциональную длине отрезка линии.

Так что выполните двоичный поиск по времени и минимизируйте ошибку, найдя время, когда A сколь угодно близко к B. На каждом этапе поиска уменьшайте размер шага поиска вдвое. 0,5, 0,25, 0,125 ... до тех пор, пока длина (closestA, closestB) не станет ниже порога ошибки, или вы откажетесь.

Это должно дать вам приемлемое приблизительное решение для простых случаев ...

Кроме того, вы сказали, что используете теорему об оси разделения в качестве «первой проверки». На самом деле для меня это звучит дорого, если это действительно «первая проверка».

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

Вы можете рассмотреть возможность использования пространственных методов, таких как грубая пространственная сетка, и проверять только те объекты, которые, как вы знаете, находятся рядом друг с другом.

Кроме того, тест сфера-сфера является очень быстрым предварительным тестом, чтобы увидеть, перекрываются ли даже ограничивающая сфера двух выпуклых объектов.

person VerdeCode    schedule 11.07.2019
comment
Нейтральный комментарий: я уже делал AABB и Boardphase, и я не против туннелирования. Я отказался от двоичного поиска, потому что он очень медленный. В любом случае это выглядит круто. - person javaLover; 12.07.2019
comment
да, часто приходится выбирать между точностью и скоростью. На практике нормально иметь дорогостоящие ветки в вашем коде, если они случаются достаточно редко для вашей ситуации. Полезно сначала реализовать самую простую вещь, которая вам может сойти с рук, и добавить комментарий // OPTIMIZEME, и, возможно, вы вернетесь к нему, может быть, вам никогда не понадобится ... - person VerdeCode; 12.07.2019

Я бы сделал это:

  1. определения

    пусть есть 2 триангулированные трехмерные выпуклые оболочки A,B. Каждый из них определил центр pc и список точек поверхности p0,p1,p2,... и список треугольников t0,t1,t2,.... Каждый треугольник должен был также вычислить его нормальное n0,n1,n2,... указывающее направление.

  2. вычислить максимальное значение внутренней точки попадания q

    поэтому давайте проверим, попадает ли A в B., просто переберите все точки A.p[i] и запомните самую внутреннюю точку, которая находится внутри B и ближайшая к B.pc (если их больше одной). Это предполагает, что ваша симуляция имеет достаточно малый временной шаг dt, поэтому интерполированные позиции не пропускают попаданий ... В противном случае необходимо использовать другой подход.

  3. вычислить смещение d

    для этого нам нужно знать направление движения A относительно B. что можно вычислить так:

    dir = (A.pc-B.pc) - (A'.pc-B'.pc)
    

    где A'.pc,B'.pc - позиции с последней итерации ... Вот изображение в относительных термах (поскольку B будет стоять на месте):

    обзор

    Теперь, чтобы вычислить смещение, просто нарисуйте луч из q с направлением -dir и проверьте, в какой треугольник B попал. Точка пересечения q' - это ваша хит-точка, которую вы ищете. Смещение:

     d = q'-q
    

    это то, что нужно перевести A, чтобы просто касаться B, а не пересекаться.

    Отсюда вы можете вычислить отражение r, поскольку треугольник попадания B имеет свою нормаль:

    отразить

    так что теперь просто переведите A на r, чтобы ваш A имел положение после отражения (чтобы время не терялось во время удара) ... Также по размеру d и относительной скорости между A,B вы можете оценить время удара и добавить трение при ударе. уменьшив скорость и r. Не забудьте также отразить скорость A. Вы также можете сделать физику удара на B.

    q' и d также могут использоваться для создания вращающего момента на обоих A,B, однако моделирование трехмерных вращений очень сложно и обычно просто имитируется углами Эйлера ... Я понимаю, что твердое тело может иметь любое количество вращений, а не только 3. ... но некоторые из них объединяются или отменяются, поэтому для имитации этого потребуется динамический список задействованных вращений, которые было бы медленно и сложно реализовать поверх него.

Взгляни на:

Вы можете найти мою реализацию point внутри convex_mesh теста и многое другое, даже _38 _... В более новой версии этого я реализовал еще больше примитивов и выпуклый_hull, но не могу опубликовать его, поскольку он увеличивается до 35 КБ, что передается 30 КБ предел уже.

Чтобы ускорить тестирование, вы также можете добавить описанную сферу к каждому convex_hull и проверить, пересекаются ли они, прежде чем тестировать сам convex_hull ... Вы знаете, что расстояние по перпендикуляру между линейными путями A, B должно быть меньше A.r+B.r ... превращаясь в простое проверка ближайшей точки линии / линии, которая также реализована по ссылке выше.

person Spektre    schedule 04.07.2020