В физическом движке процесс обнаружения столкновений разделяется на две ключевые стадии:
В библиотеке Ammo.js узкая фаза реализована на основе алгоритмов, унаследованных из Bullet Physics. Основную роль здесь играют алгоритмы GJK (Gilbert–Johnson–Keerthi) и EPA (Expanding Polytope Algorithm).
Именно на этом этапе вычисляются:
Внутри движка за обработку узкой фазы отвечает подсистема
btCollisionDispatcher. Она выбирает соответствующий
алгоритм на основе типов коллайдеров.
Основные компоненты:
btCollisionAlgorithmbtConvexConvexAlgorithmbtGjkPairDetectorbtManifoldResultbtPersistentManifoldДля выпуклых тел используется связка GJK + EPA, поскольку она универсальна и численно устойчива для большинства геометрических форм.
GJK работает исключительно с выпуклыми объектами. В Ammo.js к таким формам относятся:
btBoxShapebtSphereShapebtCapsuleShapebtCylinderShapebtConvexHullShapeКлючевым элементом алгоритма является функция поддержки (support function):
S(d) = точка формы, максимально удалённая в направлении d
В терминах Ammo.js это метод:
shape.localGetSupportingVertex(direction)
Эта операция выполняется чрезвычайно быстро и позволяет алгоритму обходиться без явного перебора граней.
Задача GJK — определить, содержит ли разность Минковского двух тел начало координат.
Разность Минковского:
A ⊖ B = { a - b | a ∈ A, b ∈ B }
Если начало координат принадлежит этой разности, значит тела пересекаются.
Выбирается произвольное направление.
Получается support-точка разности Минковского.
Строится симплекс (точка, отрезок, треугольник или тетраэдр).
Проверяется, окружает ли симплекс начало координат.
Итерации продолжаются до:
В трёхмерном пространстве используются:
Алгоритм постепенно приближается к началу координат.
Внутренне используется класс:
btGjkPairDetector
Он работает совместно с:
btVoronoiSimplexSolverbtConvexPenetrationDepthSolverGJK в Ammo.js выполняет две задачи:
Это позволяет использовать его не только для столкновений, но и для триггеров и proximity-запросов.
Алгоритм определяет только:
Однако он не вычисляет глубину проникновения, если пересечение обнаружено. Для этого требуется дополнительный алгоритм — EPA.
EPA применяется после GJK, если обнаружено пересечение.
Определить:
Если GJK завершился тетраэдром, содержащим начало координат, то EPA:
Результатом становится:
В Ammo.js последовательность выглядит так:
btGjkPairDetector определяет факт пересечения.btGjkEpaPenetrationDepthSolver.btManifoldResult.btPersistentManifold.Этот manifold хранит контактные точки между кадрами, что важно для устойчивости симуляции.
Алгоритмы GJK и EPA чувствительны к:
В Ammo.js используются:
При неправильном масштабе сцены (например, объекты размером 0.0001 или 1e6) возможны:
Рекомендуемый масштаб — объекты порядка 0.1–10 единиц.
GJK имеет сложность, зависящую от числа итераций, обычно:
EPA работает дольше, так как строит выпуклый многогранник, но применяется только при фактическом пересечении.
Оптимизация достигается за счёт:
Создание двух выпуклых тел:
const shapeA = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
const shapeB = new Ammo.btSphereShape(1);
const transformA = new Ammo.btTransform();
transformA.setIdentity();
transformA.setOrigin(new Ammo.btVector3(0, 0, 0));
const transformB = new Ammo.btTransform();
transformB.setIdentity();
transformB.setOrigin(new Ammo.btVector3(1.5, 0, 0));
После добавления тел в мир, узкая фаза автоматически выполнит:
Получение контактов:
const dispatcher = physicsWorld.getDispatcher();
const numManifolds = dispatcher.getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
const manifold = dispatcher.getManifoldByIndexInternal(i);
const numContacts = manifold.getNumContacts();
for (let j = 0; j < numContacts; j++) {
const contactPoint = manifold.getContactPoint(j);
const distance = contactPoint.getDistance();
}
}
Если distance < 0, имеется проникновение, вычисленное
через EPA.
GJK применяется только к выпуклым телам. Для невыпуклых:
btBvhTriangleMeshShape).Однако даже при работе с мешами большинство проверок сводится к выпуклым примитивам, что делает GJK центральным элементом системы.
Корректность работы GJK/EPA напрямую влияет на:
Ошибки на этом уровне приводят к:
Поэтому реализация GJK/EPA в Ammo.js максимально близка к оригиналу Bullet Physics и проверена многолетней практикой в игровых и инженерных приложениях.