Wie Nathan Reed und Teodron herausgestellt haben, lautet das Rezept zum Drehen eines Vektors v um eine Quaternion q von Einheitslänge :
1) Erstellen Sie eine reine Quaternion p aus v . Dies bedeutet einfach, eine vierte Koordinate von 0 hinzuzufügen:
p=(vx,vy,vz,0)⇔p=(v,0)
2) Multipliziere es vorab mit q und postmultipliziere es mit dem Konjugat q * :
p′=q×p×q∗
3) Dies führt zu einer weiteren reinen Quaternion, die in einen Vektor zurückverwandelt werden kann:
v′= ( p′x, p′y, p′z)
Dieser Vektor v′ ist v um q gedreht .
Das funktioniert aber alles andere als optimal . Quaternion-Multiplikationen bedeuten Tonnen und Tonnen von Operationen. Ich war neugierig auf verschiedene Implementierungen wie diese und entschied mich herauszufinden, woher diese kamen. Hier sind meine Erkenntnisse.
Wir können q auch als die Kombination eines dreidimensionalen Vektors u und eines Skalars s beschreiben :
q= ( ux, uy, uz, s ) ⇔ q= ( u , s )
Durch die Regeln der Quaternion-Multiplikation und da das Konjugat einer Quaternion mit Längeneinheit einfach umgekehrt ist, erhalten wir:
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 + s2v + s ( u × v ) + s v × ( - u ) + ( u × v ) × ( - u ) , … )
Der skalare Teil (Ellipsen) ergibt Null, wie hier beschrieben . Interessant ist der Vektorteil, AKA unser gedrehter Vektor v ' . Es kann mit einigen grundlegenden Vektoridentitäten vereinfacht werden :
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 )
Das ist jetzt viel optimaler ; Zwei-Punkt-Produkte, ein Kreuzprodukt und ein paar Extras: Etwa die Hälfte der Vorgänge. Was im Quellcode so etwas ergeben würde (unter der Annahme einer generischen Vektor-Mathematik-Bibliothek):
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);
}