Библиотека Oimo.js предоставляет мощный, но лёгкий движок физики для симуляций тел с твёрдой и мягкой структурой в браузере. Одним из ключевых аспектов при создании сложных сцен является оптимизация производительности, особенно при большом количестве объектов. Offloading симуляции в Web Worker позволяет переносить тяжёлые вычисления физики в отдельный поток, освобождая главный поток для рендеринга и UI.
Мир физики формируется через объект OIMO.World. Он
управляет всеми телами, связями и коллизиями. Основные параметры при
создании:
const world = new OIMO.World({
timestep: 1/60,
iterations: 8,
broadphase: 2, // 1 = brute force, 2 = sweep and prune
worldscale: 1,
random: true,
info: true
});
Ключевые моменты:
timestep — фиксированный шаг симуляции. Меньшие
значения повышают точность, но увеличивают нагрузку.iterations — количество итераций решателя для
столкновений. Большее число повышает стабильность, но увеличивает
вычислительную стоимость.broadphase — алгоритм предварительного отслеживания
потенциальных столкновений. Sweep-and-prune (2) эффективен для большого
числа объектов.Создание тел осуществляется через world.add:
const box = world.add({
type: 'box',
size: [1, 1, 1],
pos: [0, 5, 0],
move: true
});
Параметры type, size, pos,
move определяют форму, размеры, начальное положение и
возможность движения тела.
Web Worker создаётся отдельным файлом:
worker.js:
importScripts('oimo.js');
let world;
self.onmess age = function(e) {
const { type, data } = e.data;
if (type === 'init') {
world = new OIMO.World(data.worldOptions);
data.bodies.forEach(b => {
world.add(b);
});
} else if (type === 'step') {
world.step();
const positions = world.rigidBodies.map(b => ({
id: b.id,
position: b.getPosition(),
rotation: b.getQuaternion()
}));
self.postMessage({ type: 'upd ate', data: positions });
}
};
Особенности:
importScripts —
стандартный способ загрузки библиотек в Worker.init.step.
После каждого шага возвращается минимальный набор данных
(position и rotation) для рендера.Главный поток управляет визуализацией через Three.js, Babylon.js или
Canvas, а Worker обрабатывает физику. Коммуникация осуществляется через
postMessage:
const worker = new Worker('worker.js');
worker.postMessage({
type: 'init',
data: {
worldOptions: { timestep: 1/60, iterations: 8, broadphase: 2 },
bodies: [
{ type: 'box', size: [1,1,1], pos: [0,5,0], move: true },
{ type: 'sphere', size: [1], pos: [2,10,0], move: true }
]
}
});
function animate() {
requestAnimationFrame(animate);
worker.postMessage({ type: 'step' });
}
worker.onmess age = function(e) {
if (e.data.type === 'upd ate') {
e.data.data.forEach(obj => {
const mesh = scene.getObjectById(obj.id);
mesh.position.se t(...obj.position);
mesh.quaternion.se t(...obj.rotation);
});
}
};
animate();
Примечания:
postMessage копирует объекты, а не передаёт
ссылки).При большом количестве объектов сериализация и передача данных становятся узким местом. Рекомендации:
Float32Array) для передачи координат всех тел за один
массив.Пример передачи данных через Float32Array:
const positions = new Float32Array(world.rigidBodies.length * 7); // x,y,z,qx,qy,qz,qw
world.rigidBodies.forEach((b, i) => {
positions[i*7 + 0] = b.getPosition().x;
positions[i*7 + 1] = b.getPosition().y;
positions[i*7 + 2] = b.getPosition().z;
const q = b.getQuaternion();
positions[i*7 + 3] = q.x;
positions[i*7 + 4] = q.y;
positions[i*7 + 5] = q.z;
positions[i*7 + 6] = q.w;
});
self.postMessage({ type: 'update', data: positions }, [positions.buffer]);
Использование transferable objects позволяет передавать массивы без копирования, что значительно снижает нагрузку.
Oimo.js поддерживает различные типы соединений (joint),
например distance, hinge, ball.
Их можно создавать в Worker точно так же, как и тела:
const joint = world.add({
type: 'joint',
jointType: 'hinge',
body1: box1,
body2: box2,
pos1: [0,1,0],
pos2: [0,-1,0],
axis1: [0,0,1],
axis2: [0,0,1]
});
Всё управление происходит в Worker, а главный поток получает только финальные трансформации для визуализации.
Чтобы избежать лагов в интерфейсе:
requestAnimationFrame.60fps рендер и 120fps
физика — плавность при высокой точности.Использование Web Worker с Oimo.js повышает отзывчивость интерфейса и масштабируемость сцен, особенно в браузерных приложениях с ограниченными ресурсами.