Синхронизация кватернионов ориентации

В Oimo.js кватернионы используются для представления ориентации объектов в трехмерном пространстве. В отличие от углов Эйлера, кватернионы позволяют избегать эффекта гимбал-лок (gimbal lock) и обеспечивают плавное вращение без скачков.

Кватернион представляется как четырёхкомпонентный вектор ((x, y, z, w)), где первые три компонента определяют вектор вращения, а последняя — скалярная часть. В Oimo.js объекты типа RigidBody имеют свойства position и rotation, где rotation хранится как кватернион.

let body = world.add({
    type: 'box',
    size: [1, 1, 1],
    pos: [0, 5, 0],
    rot: [0, 0, 0, 1], // единичный кватернион
});

Единичный кватернион ([0, 0, 0, 1]) соответствует отсутствию вращения.


Синхронизация кватернионов между объектами

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

Oimo.js хранит ориентацию тела в body.rotation (кватернион) и предоставляет методы для обновления кватерниона вручную:

body.rotation.set(x, y, z, w);

Для плавной синхронизации часто используют интерполяцию кватернионов (slerp — spherical linear interpolation). Она обеспечивает равномерное вращение между двумя ориентациями:

function slerp(q1, q2, t) {
    let cosTheta = q1.dot(q2);
    if (cosTheta < 0) {
        q2 = q2.clone().negate();
        cosTheta = -cosTheta;
    }
    let angle = Math.acos(cosTheta);
    if (angle < 0.001) return q1.clone();
    let sinAngle = Math.sin(angle);
    let w1 = Math.sin((1 - t) * angle) / sinAngle;
    let w2 = Math.sin(t * angle) / sinAngle;
    return q1.clone().multiplyScalar(w1).add(q2.clone().multiplyScalar(w2));
}

Использование SLERP позволяет плавно обновлять ориентацию визуальной модели на основе физического тела:

let targetQuat = body.rotation;
mesh.quaternion.copy(slerp(mesh.quaternion, targetQuat, 0.1));

Привязка к визуальному объекту

В 3D-сценах чаще всего физическое тело RigidBody управляет движением, а графический объект (например, Three.js Mesh) синхронизируется с его кватернионом:

mesh.position.copy(body.position);
mesh.quaternion.copy(body.rotation);

Если требуется более сложная синхронизация, например с локальными смещениями, используют композицию кватернионов. Для локального вращения относительно тела:

let localQuat = new THREE.Quaternion();
localQuat.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI/4);
mesh.quaternion.copy(body.rotation).multiply(localQuat);

Такое умножение гарантирует, что локальное вращение применяется после ориентации физического тела.


Обновление кватернионов при силовых воздействиях

Физический движок Oimo.js рассчитывает новые кватернионы при применении силы или момента. После каждого шага симуляции кватернион тела обновляется автоматически. Для контроля можно считывать его в событии world.step():

world.step();
let currentQuat = body.rotation.clone();

В случаях, когда требуется влияние внешних факторов на ориентацию (например, пользовательский ввод или анимация), кватернион можно комбинировать с текущим:

let deltaQuat = new OIMO.Quat();
deltaQuat.setFromAxisAngle(new OIMO.Vec3(0,1,0), deltaAngle);
body.rotation.mul(deltaQuat);

Это позволяет добавлять вращение к существующей ориентации без разрыва непрерывности движения.


Принципы корректной синхронизации

  1. Использование единичных кватернионов — все операции должны поддерживать нормализацию, чтобы избежать дрейфа ориентации.
  2. Интерполяция вместо прямой установки — особенно при визуализации, чтобы избежать рывков.
  3. Учет локальных смещений — кватернионы должны комбинироваться в правильном порядке: сначала физическое тело, затем локальное вращение.
  4. Обновление после шага симуляции — синхронизация должна происходить после вычислений движка, иначе будут расхождения.

Примеры практических сценариев

  • Подвесные механизмы: несколько тел соединены шарнирными соединениями, визуальные модели повторяют поведение физики через кватернионы.
  • Роботизированные манипуляторы: каждая секция имеет свое тело, ориентация сегментов синхронизируется с помощью композиции кватернионов.
  • VR/AR объекты: движение контроллера передается через кватернион в движок Oimo.js, затем к визуальному объекту с интерполяцией для плавного отображения.

Вспомогательные функции для работы с кватернионами

  • Нормализация: quat.normalize()
  • Клонирование: let copy = quat.clone()
  • Инверсия: quat.invert()
  • Смешивание с другим кватернионом: quat.mul(otherQuat)
  • Создание из углов Эйлера: quat.setFromEuler(x, y, z)

Эти функции позволяют строить сложные механизмы синхронизации и анимации без потери точности и стабильности.