Ammo.js — это порт библиотеки Bullet Physics на JavaScript,
реализованный через WebAssembly. Он обеспечивает точное моделирование
физических взаимодействий в трёхмерном пространстве, включая
столкновения, трение, упругость и гравитацию. Все объекты в Ammo.js
моделируются через тела (btRigidBody),
которые помещаются в мир физики
(btDiscreteDynamicsWorld). Для вычисления
плавучести необходимо управлять этими телами вручную, добавляя силы
Архимеда, так как встроенной поддержки жидкости в Ammo.js нет.
Каждое тело характеризуется следующими параметрами:
mass) — влияет на инерцию и
отклик на силы.btCollisionShape) —
определяет объём, который контактирует с другими объектами.Сила Архимеда равна весу вытесненной жидкости. В 3D пространстве её можно представить как:
[ A = g V ]
где:
В Ammo.js вычисление V_submerged проводится вручную с
помощью разбиения объекта на сетку или вычисления по его форме
коллайдера. Для простых геометрий (куб, сфера, цилиндр) можно
использовать аналитические формулы.
Сфера:
[ V_ = ]
где (h) — высота погружения, (r) — радиус.
Куб/Параллелепипед:
[ V_ = w d h_]
где (w, d) — ширина и глубина куба, а (h_) — высота погруженной части.
Цилиндр:
[ V_ = r^2 h_]
Для добавления силы Архимеда используется метод
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);
На каждом шаге необходимо:
Такой цикл обеспечивает реалистичное поведение объектов в жидкости, включая плавание, опрокидывание и колебания.
applyCentralForce с отрицательной
компонентой скорости объекта, пропорциональной квадрату скорости.fixedTimeStep) для устойчивого поведения плавучести.Эта методика позволяет создавать реалистичное взаимодействие тел с жидкостью в Ammo.js без необходимости полной CFD-симуляции, сохраняя баланс между производительностью и физической правдоподобностью.