Кватерніони без страху
Вільям Роуен Гамільтон вирізьбив i² = j² = k² = ijk = −1 на дублінському мосту 1843 року. Кватерніони виявилися найелегантнішим способом представляти 3D-обертання: без карданного замка, з плавною сферичною інтерполяцією та лише 4 числами. Кожен ігровий рушій використовує їх усередині.
1. Від комплексних чисел до ℍ
Комплексні числа ℂ = {a + bi} розширюють ℝ однією уявною одиницею, що задовольняє i² = −1. Вони представляють 2D-обертання: множення на e^{iθ} = cos θ + i sin θ обертає точку на θ.
Прозріння Гамільтона: щоб представляти 3D-обертання, потрібні три уявні одиниці i, j, k, що задовольняють:
Кватерніони ℍ утворюють алгебру з діленням — кожен ненульовий кватерніон має мультиплікативний обернений. Це остання асоціативна алгебра з діленням (за теоремою Фробеніуса: лише ℝ, ℂ, ℍ підходять).
2. Алгебра кватерніонів
Некомутативність
pq ≠ qp загалом. Це відображає той факт, що 3D-обертання не комутують: спершу обернути X, потім Y — відрізняється від спершу Y, потім X.
Антиподальна тотожність
q та −q представляють те саме обертання. Подвійне накриття S³ → SO(3) означає, що кожному обертанню відповідають два одиничні кватерніони.
Чистий кватерніон
Кватерніон з w=0. Добутки чистих кватерніонів містять скалярний та векторний добутки 3D-векторів — причина, з якої Гамільтон їх винайшов.
Експоненційне відображення
exp(θ/2 · n̂) = cos(θ/2) + n̂ sin(θ/2). Аналогічно формулі Ейлера, це породжує всі одиничні кватерніони з осі-кута (n̂, θ).
3. Обертання через кватерніони
Щоб обернути 3D-вектор v на кут θ навколо одиничної осі n̂, формуємо одиничний кватерніон:
Кут/2 з’являється тому, що одиничні кватерніони живуть на S³ (3-сфера в ℝ⁴), яка подвійно накриває SO(3). Повне обертання на 2π відповідає проходженню q половини шляху навколо S³.
4. Карданний замок у кутах Ейлера
Кути Ейлера представляють обертання як три послідовні обертання навколо координатних осей (наприклад, конвенція ZYX: рискання, тангаж, крен). Середнє обертання на 90° вирівнює дві осі, зменшуючи ефективну кількість ступенів свободи з 3 до 2 — карданний замок:
Кватерніони не мають карданного замка, бо параметризують SO(3) глобально (за винятком антиподального ототожнення подвійного накриття) без сингулярностей. Саме тому системи керування польотом, керування орієнтацією космічних апаратів та 3D-рушії всередині використовують кватерніони.
5. Кватерніон ↔ матриця ↔ кути Ейлера
6. SLERP — сферична лінійна інтерполяція
Лінійна інтерполяція (LERP) обертань не лишається на одиничній сфері. SLERP інтерполює вздовж дуги великого кола — геодезичної — між двома одиничними кватерніонами:
SLERP дає рух зі сталою кутовою швидкістю між двома орієнтаціями — ключове для плавного панорамування камери, змішування анімації персонажів та маневрів орієнтації космічних апаратів.
7. Клас Quaternion на JavaScript
// Мінімальна бібліотека кватерніонів для 3D-обертань
class Quat {
constructor(w = 1, x = 0, y = 0, z = 0) {
this.w = w; this.x = x; this.y = y; this.z = z;
}
static fromAxisAngle(ax, ay, az, angle) {
const s = Math.sin(angle / 2);
return new Quat(Math.cos(angle / 2), ax*s, ay*s, az*s);
}
mul(q) {
return new Quat(
this.w*q.w - this.x*q.x - this.y*q.y - this.z*q.z,
this.w*q.x + this.x*q.w + this.y*q.z - this.z*q.y,
this.w*q.y - this.x*q.z + this.y*q.w + this.z*q.x,
this.w*q.z + this.x*q.y - this.y*q.x + this.z*q.w
);
}
conjugate() { return new Quat(this.w, -this.x, -this.y, -this.z); }
norm() { return Math.sqrt(this.w**2+this.x**2+this.y**2+this.z**2); }
normalise() { const n=this.norm(); return new Quat(this.w/n,this.x/n,this.y/n,this.z/n); }
// Обертаємо вектор v = {x,y,z} «сендвіч»-добутком q·v·q*
rotateVec(v) {
const p = new Quat(0, v.x, v.y, v.z);
const r = this.mul(p).mul(this.conjugate());
return {x: r.x, y: r.y, z: r.z};
}
// SLERP між this та q при параметрі t ∈ [0,1]
slerp(q, t) {
let dot = this.w*q.w + this.x*q.x + this.y*q.y + this.z*q.z;
// Гарантуємо найкоротший шлях
if (dot < 0) { q = new Quat(-q.w,-q.x,-q.y,-q.z); dot = -dot; }
if (dot > 0.9995) {
// Використовуємо NLERP для майже однакових кватерніонів
return new Quat(
this.w + t*(q.w-this.w), this.x + t*(q.x-this.x),
this.y + t*(q.y-this.y), this.z + t*(q.z-this.z)
).normalise();
}
const theta = Math.acos(dot);
const sinT = Math.sin(theta);
const s0 = Math.sin((1-t)*theta)/sinT;
const s1 = Math.sin(t*theta)/sinT;
return new Quat(s0*this.w+s1*q.w, s0*this.x+s1*q.x,
s0*this.y+s1*q.y, s0*this.z+s1*q.z);
}
toMatrix4() {
const {w,x,y,z} = this;
return [/* стовпцевий 4×4 для WebGL */
1-2*(y*y+z*z), 2*(x*y+w*z), 2*(x*z-w*y), 0,
2*(x*y-w*z), 1-2*(x*x+z*z), 2*(y*z+w*x), 0,
2*(x*z+w*y), 2*(y*z-w*x), 1-2*(x*x+y*y), 0,
0, 0, 0, 1
];
}
}
// Приклад: обернути (1,0,0) на 90° навколо осі Y → має дати (0,0,−1)
const q = Quat.fromAxisAngle(0, 1, 0, Math.PI / 2);
const v = q.rotateVec({x:1, y:0, z:0});
console.log(v); // → {x: ≈0, y: 0, z: ≈−1}
8. Застосування
-
Ігрові рушії (Unity, Unreal): клас
Quaternion— основний тип обертання. SLERP забезпечує змішування анімації та плавний рух камери. - Керування орієнтацією космічних апаратів: NASA використовує кватерніонне керування зі зворотним зв’язком (наприклад, на Voyager, МКС), бо відсутність карданного замка означає гарантовану чисельну стабільність за будь-якої орієнтації.
- Інерціальні вимірювальні блоки (IMU): інтегрування гіроскопа виконується у просторі кватерніонів — інтегруючи кутову швидкість ω оновленням q через q̇ = ½ · q · (0,ω).
- Подвійні кватерніони: розширення, що кодує і обертання, і перенесення у 8 числах. Використовуються у скелетній анімації для природнішого «згинання» скінінгових сіток, ніж матриці.
- Кватерніонні нейронні мережі: заміна дійснозначних ваг кватерніонними може зменшити кількість параметрів учетверо й нативно кодувати 3D-обертальну еквіваріантність.
THREE.Quaternion з setFromAxisAngle,
multiply, slerp та toArray — ті
самі операції, що й у нашому класі вище, оптимізовані для WebGL.