Профилирование физического цикла

Физический движок Cannon.js реализует симуляцию тел в реальном времени, используя пошаговое обновление состояния объектов. Физический цикл состоит из последовательных этапов: интеграция движения, обработка столкновений и применение внешних сил. В Cannon.js управление циклом осуществляется методом world.step(dt), где dt — шаг времени. Этот метод обновляет все физические объекты сцены, рассчитывает новые позиции, скорости и вращения с учётом сил и ограничений.

Cannon.js использует фиксированный шаг времени для стабильности симуляции, что особенно важно при сложных взаимодействиях и высоких скоростях объектов. Типичная конфигурация шага:

const fixedTimeStep = 1.0 / 60.0; // 60 FPS
const maxSubSteps = 3;
world.step(fixedTimeStep, deltaTime, maxSubSteps);
  • deltaTime — фактический прошедший промежуток времени между кадрами.
  • maxSubSteps — максимальное число внутренних шагов, которые можно выполнить, если реальное время между кадрами превышает фиксированный шаг.

Использование фиксированного шага гарантирует корректное решение системы дифференциальных уравнений движения.


Подробная структура цикла

Физический цикл состоит из нескольких последовательных фаз:

  1. Применение внешних сил Каждое тело получает силы от гравитации, столкновений или пользовательских воздействий. В Cannon.js сила добавляется через метод body.applyForce(force, relativePoint).

  2. Интеграция Cannon.js применяет интегратор Velocity Verlet для обновления позиций и скоростей тел. Этот метод обеспечивает баланс между точностью и производительностью:

    // псевдокод интегратора
    velocity += force / mass * dt;
    position += velocity * dt;

    Угол поворота и угловая скорость интегрируются аналогично с использованием момента инерции и кватернионов.

  3. Обнаружение столкновений Движок строит широкое дерево поиска (broadphase) для быстрого выявления потенциальных коллизий между телами. В Cannon.js доступны:

    • NaiveBroadphase — полный перебор всех объектов, подходит для малых сцен.
    • SAPBroadphase — Sweep and Prune, оптимизированный для линейно расположенных тел.
  4. Решение столкновений (narrowphase) После broadphase вычисляются точные точки контакта и нормали столкновений. Cannon.js применяет метод Лагранжа с ограничениями для обеспечения непрерывности контактов и предотвращения проникновения объектов.

  5. Применение импульсов и ограничений После расчёта контактов движок корректирует скорости и позиции тел, чтобы соблюсти физические законы (закон сохранения импульса и углового момента). Ограничения, такие как соединения и шарниры, обрабатываются на этом этапе.

  6. Обновление состояния тела Позиции и ориентации обновляются, и готовятся для рендеринга. Кватернионы нормализуются для предотвращения численных ошибок.


Профилирование производительности

Профилирование физического цикла важно для сложных сцен с сотнями объектов. В Cannon.js можно измерять время выполнения ключевых фаз:

console.time("physics");
world.step(fixedTimeStep);
console.timeEnd("physics");

Для более детального анализа можно добавить временные метки на каждый этап: интеграция, broadphase, narrowphase, решение ограничений.

Основные показатели для анализа:

  • broadphase time — время построения списка потенциальных столкновений.
  • narrowphase time — точное вычисление точек контакта.
  • solver time — время применения ограничений и импульсов.
  • integration time — обновление позиций и скоростей.

Оптимизация цикла

  1. Регулировка шага времени и substeps Меньший шаг времени повышает точность, но увеличивает нагрузку на CPU. Использование maxSubSteps позволяет сглаживать вариации кадров без потери стабильности.

  2. Выбор broadphase Для крупных сцен с тысячами объектов предпочтительно использовать SAPBroadphase вместо NaiveBroadphase.

  3. Декомпозиция сложных тел Сложные меши следует заменять на комбинацию простых примитивов (Box, Sphere, Cylinder). Это снижает количество коллизий и ускоряет narrowphase.

  4. Активные и спящие тела Cannon.js поддерживает спящий режим (sleeping). Тела, не участвующие в движении, не обрабатываются физическим циклом, что экономит ресурсы.

body.allowSleep = true;
body.sleepSpeedLimit = 0.1;
body.sleepTimeLimit = 1.0;
  1. Использование профайлера и визуализация контактов Можно визуализировать коллизии и ограничения через ContactMaterial и отладочные объекты. Это помогает выявить узкие места и некорректные расчёты.

Практическая демонстрация

const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
world.broadphase = new CANNON.SAPBroadphase(world);
world.solver.iterations = 10;

const ground = new CANNON.Body({
  mass: 0,
  shape: new CANNON.Plane(),
});
world.addBody(ground);

const box = new CANNON.Body({
  mass: 1,
  shape: new CANNON.Box(new CANNON.Vec3(1,1,1)),
  position: new CANNON.Vec3(0, 10, 0)
});
world.addBody(box);

function update(dt) {
  world.step(1/60, dt, 3);
  console.log(box.position);
}

В этой конфигурации цикл демонстрирует применение всех этапов физического обновления: силы гравитации, интеграцию, проверку столкновений и корректировку позиций.

Понимание структуры и профилирования физического цикла в Cannon.js позволяет создавать реалистичные симуляции с высокой производительностью и точностью, избегая ошибок численной интеграции и коллизий.