Полная схема интеграции с Three.js

Ammo.js — это порт библиотеки Bullet Physics на JavaScript с использованием asm.js и WebAssembly, предоставляющий физический движок для браузера. Интеграция с Three.js позволяет создавать реалистичную физику для 3D-сцен: столкновения, гравитацию, силу трения, пружины и жесткие тела.

Взаимодействие двух библиотек строится на разделении ролей: Three.js отвечает за визуализацию, а Ammo.js — за физическую симуляцию. Сценарий работы обычно выглядит следующим образом:

  1. Создание физического мира (btDiscreteDynamicsWorld).
  2. Создание объектов с физическими телами (btRigidBody) и коллизиями (btCollisionShape).
  3. Связка этих объектов с визуальными мешами в Three.js.
  4. Обновление позиций мешей после каждого шага физической симуляции.

Настройка физического мира

Создание физического мира требует нескольких компонентов:

// Создание конфигурации коллизий
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

Для синхронизации физики с визуализацией создаётся меш в 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 позволяет создавать соединения между телами:

  • Hinge (btHingeConstraint) — вращение вокруг оси.
  • Point-to-Point (btPoint2PointConstraint) — шарнирная точка.
  • Slider (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) значительно ускоряет проверку столкновений для сложных сцен.


Шаги интеграции в рендер-цикл

  1. Вычисление deltaTime.
  2. Вызов physicsWorld.stepSimulation(deltaTime, maxSubSteps).
  3. Обновление позиции и кватернионов всех мешей.
  4. Рендер сцены через Three.js.

Эта схема позволяет синхронизировать физику с графикой без рассинхронизации или “прыгающих” объектов.


Ammo.js в сочетании с Three.js предоставляет мощный инструмент для создания интерактивных 3D-приложений, игр и симуляций с реалистичной физикой, позволяя полностью контролировать динамику объектов, столкновения и связи между телами.