Имитация плавучести: силы Архимеда вручную

Ammo.js — это порт библиотеки Bullet Physics на JavaScript, реализованный через WebAssembly. Он обеспечивает точное моделирование физических взаимодействий в трёхмерном пространстве, включая столкновения, трение, упругость и гравитацию. Все объекты в Ammo.js моделируются через тела (btRigidBody), которые помещаются в мир физики (btDiscreteDynamicsWorld). Для вычисления плавучести необходимо управлять этими телами вручную, добавляя силы Архимеда, так как встроенной поддержки жидкости в Ammo.js нет.

Каждое тело характеризуется следующими параметрами:

  • Масса (mass) — влияет на инерцию и отклик на силы.
  • Форма коллайдера (btCollisionShape) — определяет объём, который контактирует с другими объектами.
  • Плотность и центр масс — критичны для расчёта подъемной силы.
  • Локальная ориентация и позиция — используются при вычислении погружения в жидкость.

Моделирование силы Архимеда

Сила Архимеда равна весу вытесненной жидкости. В 3D пространстве её можно представить как:

[ A = g V ]

где:

  • () — плотность жидкости,
    1. — ускорение свободного падения,
  • (V_) — объём погруженной части объекта,
  • () — направление вверх (вектор силы).

В Ammo.js вычисление V_submerged проводится вручную с помощью разбиения объекта на сетку или вычисления по его форме коллайдера. Для простых геометрий (куб, сфера, цилиндр) можно использовать аналитические формулы.

Расчёт погруженного объёма для стандартных форм

Сфера:

[ V_ = ]

где (h) — высота погружения, (r) — радиус.

Куб/Параллелепипед:

[ V_ = w d h_]

где (w, d) — ширина и глубина куба, а (h_) — высота погруженной части.

Цилиндр:

[ V_ = r^2 h_]

Применение силы в Ammo.js

Для добавления силы Архимеда используется метод applyCentralForce объекта btRigidBody. Сила должна обновляться каждый кадр симуляции:

const force = new Ammo.btVector3(0, buoyantForce, 0);
rigidBody.applyCentralForce(force);

buoyantForce вычисляется на основе текущего погружения тела.

Для корректного эффекта важно учитывать точку приложения силы. Для сложных тел подъемная сила обычно распределяется на несколько точек, чтобы избежать неестественного вращения. В Ammo.js это достигается добавлением нескольких силовых векторов на локальные позиции через applyForce(force, rel_pos).

const localPoint = new Ammo.btVector3(x, y, z);
rigidBody.applyForce(forceVector, localPoint);

Интерфейс с водой и проверка погружения

Необходимо задавать уровень поверхности жидкости. Например, плоскость y = waterLevel. Для расчёта h_submerged:

const objectBottomY = objectPosition.y - halfHeight;
const hSubmerged = Math.max(0, waterLevel - objectBottomY);

Если hSubmerged равен нулю — объект полностью вне воды. При этом buoyantForce равна нулю.

Динамическая регулировка силы в зависимости от плотности

Для разных жидкостей плотность () варьируется. Например:

const waterDensity = 1000; // кг/м³
const gravity = 9.81;
const buoyantForce = waterDensity * gravity * submergedVolume;

Эта сила затем передаётся в applyCentralForce.

Интеграция с шагами симуляции

Ammo.js использует пошаговое обновление физического мира через stepSimulation:

physicsWorld.stepSimulation(deltaTime, maxSubSteps);

На каждом шаге необходимо:

  1. Определить уровень жидкости относительно объекта.
  2. Рассчитать погружённый объём.
  3. Вычислить силу Архимеда.
  4. Применить силу к телу.

Такой цикл обеспечивает реалистичное поведение объектов в жидкости, включая плавание, опрокидывание и колебания.

Учет дополнительных факторов

  • Дрейф и сопротивление жидкости: сила сопротивления можно моделировать через applyCentralForce с отрицательной компонентой скорости объекта, пропорциональной квадрату скорости.
  • Плотность тела vs плотность жидкости: если плотность тела меньше жидкости, объект будет плавать на поверхности, иначе — тонуть.
  • Распределение массы и центр тяжести: объекты с низким центром тяжести будут стабильно удерживаться вертикально, а с высоким — могут переворачиваться.

Советы по оптимизации

  1. Для больших сцен с множеством объектов не вычислять объём для каждой точки поверхности. Использовать аппроксимации по форме.
  2. Кэшировать формы и размеры коллайдеров для повторного использования.
  3. Силы применять локально на центр масс или на ограниченное число точек для экономии ресурсов.
  4. Использовать фиксированный шаг симуляции (fixedTimeStep) для устойчивого поведения плавучести.

Эта методика позволяет создавать реалистичное взаимодействие тел с жидкостью в Ammo.js без необходимости полной CFD-симуляции, сохраняя баланс между производительностью и физической правдоподобностью.