Jak ujawnili Nathan Reed i teodron, receptą na obrót wektora v o czwartorzędową jednostkę długości q jest:
1) Utwórz czysty czwartorzęd p z v . Oznacza to po prostu dodanie czwartej współrzędnej 0:
p = ( vx, vy,vz, 0 ) ⇔ p = ( v , 0 )
2) Pomnóż go wstępnie przez q, a następnie pomnóż go przez koniugat q * :
p′=q× p ×q∗
3) Spowoduje to powstanie kolejnego czystego czwartorzędu, który można odwrócić do wektora:
v′= ( p′x, p′y, p′z)
Ten wektor jest obrócony o .v′vq
To działa, ale dalekie od optymalnego . Mnożenia czwartorzędu oznaczają tony operacji. Byłem ciekawy różnych wdrożeń, takich jak ten , i postanowiłem dowiedzieć się, skąd one pochodzą. Oto moje ustalenia.
Możemy również opisać q jako połączenie trójwymiarowego wektora u i skalara s :
q= ( ux, uy, uz, s ) ⇔ q= ( u , s )
Zgodnie z regułami mnożenia czwartorzędu i ponieważ koniugat czwartorzędu o długości jednostki jest po prostu odwrotny, otrzymujemy:
p′= qp 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 + s2)v+s(u×v)+sv×(−u)+(u×v)×(−u),…)
Część skalarna (elipsy) daje zero, jak wyszczególniono tutaj . Interesująca jest część wektorowa, AKA nasz obrócony wektor v ' . Można to uprościć za pomocą podstawowych tożsamości wektorowych :
v′= ( u ⋅ v ) u + s2)v + s ( u × v ) + s ( u × v ) + u × ( u × v )= ( u ⋅ v ) u + s2)v + 2 s ( u × v ) + ( u ⋅ v ) u - ( u ⋅ u ) v= 2 ( u ⋅ v ) u + ( s2)- u ⋅ u ) v + 2 s ( u × v )
Jest to teraz o wiele bardziej optymalne ; dwa produkty kropkowe, produkt krzyżowy i kilka dodatków: około połowa operacji. Co dałoby coś takiego w kodzie źródłowym (przy założeniu jakiejś ogólnej biblioteki matematycznej wektorowej):
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);
}