Quaternion и вращение без блокировки осей

Quaternion — это математическая структура, используемая для описания вращений в трёхмерном пространстве без проблем, связанных с Gimbal Lock (блокировкой осей). В Three.js кватернионы реализованы через класс THREE.Quaternion. Они позволяют плавно интерполировать вращения и комбинировать их, сохраняя стабильность и корректность во всех ориентациях.

Кватернион представляет собой четыре компонента: x, y, z, w, где w — скалярная часть, а x, y, z — векторная часть. Основная формула:

[ q = w + xi + yj + zk]

В Three.js доступна и удобная работа с кватернионами через методы setFromAxisAngle, setFromEuler, multiply, slerp и другие.


Создание и применение кватернионов

Инициализация кватерниона

const quaternion = new THREE.Quaternion(); // по умолчанию идентичный (без вращения)

Можно задать вращение вокруг произвольной оси:

const axis = new THREE.Vector3(0, 1, 0); // ось Y
const angle = Math.PI / 4; // 45 градусов
quaternion.setFromAxisAngle(axis, angle);

Для применения к объекту используется свойство object.quaternion:

mesh.quaternion.copy(quaternion);

При этом не требуется использовать матрицы напрямую, что упрощает работу с вращениями и предотвращает накопление ошибок.


Преобразование между Euler и Quaternion

Хотя кватернионы обеспечивают стабильное вращение, иногда удобнее задавать вращение через углы Эйлера. В Three.js это делается через класс THREE.Euler:

const euler = new THREE.Euler(Math.PI / 2, 0, 0, 'XYZ');
mesh.quaternion.setFromEuler(euler);

Важно помнить, что последовательность осей (‘XYZ’, ‘YXZ’ и т.д.) влияет на результат. Использование кватернионов полностью устраняет эффекты Gimbal Lock, которые возникают при последовательной работе с углами Эйлера.


Композиция вращений

Комбинирование вращений через кватернионы производится методом multiply:

const q1 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), Math.PI/4);
const q2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0), Math.PI/4);

const qCombined = q1.clone().multiply(q2); // сначала q1, затем q2
mesh.quaternion.copy(qCombined);

Метод multiply не коммутативен: порядок умножения влияет на итоговое вращение.


Интерполяция вращений (Slerp)

Для плавного перехода между двумя ориентациями используется slerp (spherical linear interpolation):

const startQuat = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
const endQuat = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI/2, Math.PI/2, 0));

THREE.Quaternion.slerp(startQuat, endQuat, mesh.quaternion, 0.5); // 50% между start и end

Slerp обеспечивает равномерное вращение по кратчайшему пути на 4D-сфере кватернионов, предотвращая резкие рывки и перекручивание объектов.


Вращение объекта с учётом текущего положения

Кватернионы позволяют вращать объект относительно его локальной системы координат без накопления ошибок. Пример вращения вокруг локальной оси Y:

const axis = new THREE.Vector3(0, 1, 0); // локальная ось
const angle = 0.01; // небольшое приращение
const deltaQuat = new THREE.Quaternion().setFromAxisAngle(axis, angle);

mesh.quaternion.multiply(deltaQuat); // применение инкрементного вращения

Использование локальных осей через кватернионы делает анимации и управление камерой более предсказуемыми по сравнению с поворотами через rotation.x/y/z.


Применение кватернионов для камеры

Для управления камерой без блокировки осей удобно использовать кватернионы:

const cameraQuat = new THREE.Quaternion();
cameraQuat.setFromEuler(new THREE.Euler(pitch, yaw, 0, 'YXZ'));
camera.quaternion.copy(cameraQuat);

Комбинация pitch и yaw через кватернионы предотвращает ситуацию, когда камера теряет одну степень свободы (Gimbal Lock) при вертикальном вращении.


Полезные методы класса THREE.Quaternion

  • setFromAxisAngle(axis, angle) — установка вращения вокруг произвольной оси.
  • setFromEuler(euler) — преобразование из углов Эйлера.
  • multiply(q) — комбинирование вращений.
  • slerp(qb, t) — интерполяция кватернионов.
  • normalize() — нормализация кватерниона для предотвращения искажения вращений.
  • inverse() — получение обратного вращения.
  • dot(q) — скалярное произведение, полезно для определения близости ориентаций.

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

  1. Избегать смешивания rotation и quaternion — использование одновременно свойств rotation и quaternion может приводить к непредсказуемым результатам.
  2. Нормализовать кватернионы после множества инкрементных вращений — это предотвращает накопление численных ошибок.
  3. Использовать slerp для анимаций — гарантирует плавность и корректность вращений при интерполяции.
  4. Предпочитать кватернионы при управлении камерой и персонажами — особенно когда требуется полная свобода вращения в пространстве.

Кватернионы являются фундаментальной частью современного 3D-программирования. В Three.js их использование позволяет создавать стабильные, плавные и предсказуемые вращения объектов и камеры, избегая ограничений классической модели Эйлера и Gimbal Lock.