Ammo.js — это порт библиотеки Bullet Physics на JavaScript с использованием asm.js и WebAssembly, предоставляющий физический движок для браузера. Интеграция с Three.js позволяет создавать реалистичную физику для 3D-сцен: столкновения, гравитацию, силу трения, пружины и жесткие тела.
Взаимодействие двух библиотек строится на разделении ролей: Three.js отвечает за визуализацию, а Ammo.js — за физическую симуляцию. Сценарий работы обычно выглядит следующим образом:
btDiscreteDynamicsWorld).btRigidBody) и
коллизиями (btCollisionShape).Создание физического мира требует нескольких компонентов:
// Создание конфигурации коллизий
const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
const overlappingPairCache = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
// Создание физического мира
const physicsWorld = new Ammo.btDiscreteDynamicsWorld(
dispatcher,
overlappingPairCache,
solver,
collisionConfiguration
);
physicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));
Ключевые моменты:
btDefaultCollisionConfiguration управляет настройками
алгоритмов обнаружения столкновений.btCollisionDispatcher отвечает за обработку
столкновений.btDbvtBroadphase ускоряет поиск столкновений для
больших сцен.btSequentialImpulseConstraintSolver реализует пошаговое
решение сил для тел.setGravity задаёт общий
вектор силы.Каждое физическое тело состоит из формы коллизии и жёсткого тела:
// Создание формы коллизии
const boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
// Масса и трансформация
const mass = 1;
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 5, 0));
// Рассчёт инерции
const localInertia = new Ammo.btVector3(0, 0, 0);
if (mass !== 0) boxShape.calculateLocalInertia(mass, localInertia);
// Создание движения тела
const motionState = new Ammo.btDefaultMotionState(transform);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, boxShape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
// Добавление тела в мир
physicsWorld.addRigidBody(body);
Особенности:
0 создаёт статическое тело, не поддающееся
силам.calculateLocalInertia важно для динамических объектов,
чтобы правильно моделировались вращения.btDefaultMotionState хранит текущую трансформацию тела
и синхронизирует её с визуальными мешами.Для синхронизации физики с визуализацией создаётся меш в Three.js, и его позиция и ориентация обновляются после каждого шага симуляции:
// Создание меша
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// Обновление в рендер-цикле
function updatePhysics(deltaTime) {
physicsWorld.stepSimulation(deltaTime, 10);
const ms = body.getMotionState();
if (ms) {
const transform = new Ammo.btTransform();
ms.getWorldTransform(transform);
const p = transform.getOrigin();
const q = transform.getRotation();
mesh.position.set(p.x(), p.y(), p.z());
mesh.quaternion.set(q.x(), q.y(), q.z(), q.w());
}
}
Ключевые аспекты синхронизации:
stepSimulation продвигает физику на заданное
время.10) — максимальное количество
внутренних субшагов, увеличивающее точность столкновений при низкой
частоте кадров.Ammo.js поддерживает разнообразные формы коллизий:
btBoxShape — куб, прямоугольник.btSphereShape — сфера.btCylinderShape — цилиндр.btCapsuleShape — капсула для персонажей.btConvexHullShape — произвольная выпуклая
геометрия.Для управления взаимодействием тел используются фильтры коллизий:
const group = 1; // принадлежность к группе
const mask = 1 | 2; // взаимодействие с группами 1 и 2
physicsWorld.addRigidBody(body, group, mask);
Фильтры позволяют исключить лишние проверки между объектами и оптимизируют производительность.
Для динамических объектов можно применять силу, импульс и момент:
// Добавление силы
body.applyCentralForce(new Ammo.btVector3(0, 100, 0));
// Импульс
body.applyCentralImpulse(new Ammo.btVector3(10, 0, 0));
// Момент силы
body.applyTorque(new Ammo.btVector3(0, 50, 0));
Примечание: applyCentralForce действует
непрерывно на каждом шаге, а applyCentralImpulse — разово,
мгновенно изменяя скорость тела.
Ammo.js позволяет создавать соединения между телами:
btHingeConstraint) — вращение
вокруг оси.btPoint2PointConstraint) — шарнирная точка.btSliderConstraint) — линейное
скольжение.Пример простого шарнирного соединения:
const pivotA = new Ammo.btVector3(0, 1, 0);
const pivotB = new Ammo.btVector3(0, -1, 0);
const hinge = new Ammo.btHingeConstraint(bodyA, bodyB, pivotA, pivotB, true);
physicsWorld.addConstraint(hinge, true);
Констрейнты используются для создания механических систем: дверей, роботов, маятников.
btDbvtBroadphase и фильтров групп.stepSimulation) —
фиксированное время шага более стабильно.Ammo.js с Three.js может обрабатывать сотни тел в реальном времени при аккуратной настройке этих параметров.
Для сложных форм используется btConvexHullShape или
btBvhTriangleMeshShape для статических мешей. Это позволяет
импортировать геометрию из моделей и корректно вычислять столкновения
без упрощений.
const triangleMesh = new Ammo.btTriangleMesh();
geometry.attributes.position.array.forEach((v, i) => {
// Добавление вершин в mesh
});
const shape = new Ammo.btBvhTriangleMeshShape(triangleMesh, true);
Использование BVH-структур (Bounding Volume Hierarchy) значительно ускоряет проверку столкновений для сложных сцен.
physicsWorld.stepSimulation(deltaTime, maxSubSteps).Эта схема позволяет синхронизировать физику с графикой без рассинхронизации или “прыгающих” объектов.
Ammo.js в сочетании с Three.js предоставляет мощный инструмент для создания интерактивных 3D-приложений, игр и симуляций с реалистичной физикой, позволяя полностью контролировать динамику объектов, столкновения и связи между телами.