Решатель ограничений

Решатель ограничений (Constraint Solver) является ключевым компонентом физического движка Ammo.js. Его задача — корректно рассчитывать взаимодействия между телами с учётом физических ограничений, таких как соединения, столкновения и сцепления. В основе лежит метод численного интегрирования, который обеспечивает стабильность симуляции при высокой скорости и сложных сценах.

Ammo.js использует Bullet Physics как основу, поэтому концепции решателя полностью соответствуют Bullet, включая итерационные методы решения систем ограничений и контактов.


Типы ограничений

В Ammo.js ограничения делятся на несколько категорий:

  1. Контактные ограничения (Contact Constraints) Определяют реакции при столкновении тел. Сюда входят нормальные силы, силы трения и упругости. Основная цель — предотвратить проникновение объектов и корректно передать импульсы.

  2. Жёсткие соединения (Hard Constraints) Примеры: шарнир (Hinge), фиксированное соединение (Fixed Constraint), пружины. Эти ограничения ограничивают относительное движение двух тел.

  3. Мягкие ограничения (Soft Constraints) Позволяют некоторую свободу движения для имитации гибкости или упругости. Используются для риггинга мягких тел или элементов сцепления.


Основные классы и методы

Ammo.js предоставляет следующие ключевые классы для работы с ограничениями:

  • btTypedConstraint — базовый класс всех ограничений. От него наследуются конкретные типы, такие как Hinge или Point2Point.
  • btHingeConstraint — шарнирное соединение с возможностью вращения вокруг одной оси.
  • btPoint2PointConstraint — точечное соединение, удерживающее два тела в одной точке.
  • btGeneric6DofConstraint — универсальное 6-степенное ограничение, позволяющее задавать ограничения по каждой оси независимо.

Ключевые методы, влияющие на работу решателя:

  • enableFeedback(bool) — активирует вычисление силы, действующей на соединение.
  • setLimit(min, max) — задаёт границы движения для шарнирных соединений.
  • getAppliedImpulse() — возвращает силу, применённую решателем в последней симуляции.

Настройка и работа решателя

Решатель ограничений рассчитывает силы на каждом шаге симуляции. Основной алгоритм включает несколько этапов:

  1. Формирование системы ограничений Для всех тел и соединений строится система линейных уравнений с учётом массы, инерции и контактов.

  2. Итеративное решение Ammo.js использует метод Projected Gauss-Seidel (PGS) для итерационного решения ограничений. Количество итераций сильно влияет на стабильность и точность:

    dynamicsWorld.getSolverInfo().m_numIterations = 20;
  3. Применение импульсов к телам После итераций решатель корректирует скорости и позиции тел, чтобы ограничения были выполнены.


Связь с коллизиями и телами

Для корректной работы решателя необходимо, чтобы все тела имели правильно настроенные RigidBody параметры:

  • Масса (mass) — влияет на распределение сил.
  • Матрица инерции (inertia) — определяет, как тело сопротивляется вращению.
  • Флаги активации (activationState) — позволяют оптимизировать работу решателя, исключая неподвижные объекты.

Коллизии формируют contact manifolds, которые затем добавляются в систему ограничений. Решатель обрабатывает их наряду с пользовательскими соединениями.


Параметры оптимизации

Качество симуляции зависит от нескольких параметров:

  • Количество итераций (m_numIterations) — увеличение повышает точность, но снижает производительность.
  • ERP (Error Reduction Parameter) — контролирует, насколько быстро решатель исправляет ошибки нарушения ограничений.
  • CFM (Constraint Force Mixing) — позволяет “размягчать” ограничения для предотвращения нестабильности при жёстких соединениях.

Пример настройки решателя:

let solver = new Ammo.btSequentialImpulseConstraintSolver();
dynamicsWorld.setConstraintSolver(solver);

let info = dynamicsWorld.getSolverInfo();
info.set_m_numIterations(20);
info.set_m_erp(0.2);
info.set_m_globalCfm(1e-5);

Практические советы

  • Для сложных сцена с множеством шарниров и контактов важно постепенно увеличивать итерации, чтобы избежать “дрожания” объектов.
  • Использование Feedback позволяет визуализировать силы в соединениях и отлаживать поведение конструкции.
  • Мягкие тела и сложные системы с большой разницей масс требуют аккуратного подбора CFM и ERP, иначе симуляция может стать нестабильной.

Примеры использования

Шарнирное соединение

let hinge = new Ammo.btHingeConstraint(
    bodyA, 
    bodyB, 
    pivotA, 
    pivotB, 
    axisA, 
    axisB,
    true
);
hinge.setLimit(-Math.PI/4, Math.PI/4);
dynamicsWorld.addConstraint(hinge, true);

Точечное соединение

let p2p = new Ammo.btPoint2PointConstraint(bodyA, bodyB, pivotA, pivotB);
dynamicsWorld.addConstraint(p2p, true);

Универсальное 6-DOF соединение

let dof = new Ammo.btGeneric6DofConstraint(bodyA, bodyB, frameInA, frameInB, true);
dof.setLinearLowerLimit(new Ammo.btVector3(0,0,0));
dof.setLinearUpperLimit(new Ammo.btVector3(0,0,0));
dof.setAngularLowerLimit(new Ammo.btVector3(-Math.PI/4, -Math.PI/4, -Math.PI/4));
dof.setAngularUpperLimit(new Ammo.btVector3(Math.PI/4, Math.PI/4, Math.PI/4));
dynamicsWorld.addConstraint(dof, true);

Решатель ограничений в Ammo.js обеспечивает точную и стабильную симуляцию физики, позволяя управлять контактами, шарнирами и сложными соединениями. Настройка его параметров, таких как количество итераций, ERP и CFM, напрямую влияет на реализм и производительность. Комбинация правильного выбора ограничений и оптимизации решателя позволяет создавать как простые физические сцены, так и сложные механизмы с множеством взаимодействующих тел.