Как показали Натан Рид и теодрон, рецепт вращения вектора v кватернионом единичной длины q :
1) Создайте чистый кватернион p из v . Это просто означает добавление четвертой координаты 0:
р = ( вИкс, vY, vZ, 0 ) ⇔ p = ( v , 0 )
2) Предварительно умножьте его на q и затем умножьте на сопряженное q * :
п'= q× p × q*
3) Это приведет к еще одному чистому кватерниону, который можно превратить в вектор:
v'= ( р'Икс, р'Y, р'Z)
Этот вектор v' является v повернута на Q .
Это работает, но далеко не оптимально . Умножения кватернионов означают тонны и тонны операций. Мне было любопытно узнать о различных реализациях, таких как эта , и я решил выяснить, откуда они пришли. Вот мои выводы.
Мы также можем описать q как комбинацию трехмерного вектора u и скаляра s :
Q= ( тыИкс, уY, уZ, Ев ) ⇔ д= ( ты , с )
По правилам умножения кватернионов , а так как сопряженность кватерниона единичной длины просто обратна, мы получаем:
п'= qр д*= ( u , s ) ( v , 0 ) ( - u , s )= ( s v + u × v , - u ⋅ v ) ( - u , s )= ( ( - u ⋅ v ) ( - u ) + s ( s v + u × v ) + ( s v + u × v ) × ( - u ) , … ) = ( ( u ⋅ v ) u + s2v + s ( u × v ) + s v × ( - u ) + ( u × v ) × ( - u ) , … )
Скалярная часть (эллипсы) приводит к нулю, как подробно описано здесь . Что интересно, векторная часть, AKA наш повернутый вектор v ' . Это можно упростить, используя некоторые основные векторные тождества :
v'= ( u ⋅ v ) u + s2v + s ( u × v ) + s ( u × v ) + u × ( u × v )= ( u ⋅ v ) u + s2v + 2 s ( u × v ) + ( u ⋅ v ) u - ( u ⋅ u ) v= 2 ( u ⋅ v ) u + ( s2- u ⋅ u ) v + 2 s ( u × v )
Это теперь намного более оптимально ; две точки, перекрестный продукт и несколько дополнительных функций: около половины операций. Что даст что-то подобное в исходном коде (при условии некоторой универсальной библиотеки векторной математики)
void rotate_vector_by_quaternion(const Vector3& v, const Quaternion& q, Vector3& vprime)
{
// Extract the vector part of the quaternion
Vector3 u(q.x, q.y, q.z);
// Extract the scalar part of the quaternion
float s = q.w;
// Do the math
vprime = 2.0f * dot(u, v) * u
+ (s*s - dot(u, u)) * v
+ 2.0f * s * cross(u, v);
}