Es gibt mehr als eine Möglichkeit, dies zu tun. Sie können die absolute Ausrichtung oder die Drehung relativ zu Ihrem Avatar berechnen, dh Ihre neue Ausrichtung = avatarOrientation * q. Hier ist der letztere:
Berechnen Sie die Rotationsachse, indem Sie das Kreuzprodukt des Einheitsvorwärtsvektors Ihres Avatars und den Einheitsvektor vom Avatar zum Ziel nehmen, den neuen Vorwärtsvektor:
vector newForwardUnit = vector::normalize(target - avatarPosition);
vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
Berechnen Sie den Drehwinkel mit dem Skalarprodukt
float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
Erstellen Sie die Quaternion mit rotAxis und rotAngle und multiplizieren Sie sie mit der aktuellen Ausrichtung des Avatars
quaternion q(rotAxis, rotAngle);
quaternion newRot = avatarRot * q;
Wenn Sie Hilfe beim Auffinden des aktuellen Vorwärtsvektors des Avatars benötigen, geben Sie den Wert für 1 ein.
BEARBEITEN: Die Berechnung der absoluten Orientierung ist tatsächlich etwas einfacher. Verwenden Sie als Eingabe für 1) und 2) den Vorwärtsvektor der Identitätsmatrix anstelle des Avatars. Und multipliziere es nicht mit 3), sondern verwende es direkt als neue Orientierung:newRot = q
Wichtig zu beachten: Die Lösung weist zwei Anomalien auf, die durch die Art des Kreuzprodukts verursacht werden:
Wenn die Vorwärtsvektoren gleich sind. Lösung hier ist einfach die Identität Quaternion zurückzugeben
Wenn die Vektoren genau in die entgegengesetzte Richtung zeigen. Die Lösung besteht darin, das Quaternion mit der Aufwärtsachse des Avatars als Rotationsachse und dem Winkel von 180,0 Grad zu erstellen.
Hier ist die Implementierung in C ++, die sich um diese Randfälle kümmert. Das Konvertieren in C # sollte einfach sein.
// returns a quaternion that rotates vector a to vector b
quaternion get_rotation(const vector &a, const vector &b, const vector &up)
{
ASSERT_VECTOR_NORMALIZED(a);
ASSERT_VECTOR_NORMALIZED(b);
float dot = vector::dot(a, b);
// test for dot -1
if(nearly_equal_eps_f(dot, -1.0f, 0.000001f))
{
// vector a and b point exactly in the opposite direction,
// so it is a 180 degrees turn around the up-axis
return quaternion(up, gdeg2rad(180.0f));
}
// test for dot 1
else if(nearly_equal_eps_f(dot, 1.0f, 0.000001f))
{
// vector a and b point exactly in the same direction
// so we return the identity quaternion
return quaternion(0.0f, 0.0f, 0.0f, 1.0f);
}
float rotAngle = acos(dot);
vector rotAxis = vector::cross(a, b);
rotAxis = vector::normalize(rotAxis);
return quaternion(rotAxis, rotAngle);
}
EDIT: Korrigierte Version von Marc's XNA Code
// the new forward vector, so the avatar faces the target
Vector3 newForward = Vector3.Normalize(Position - GameState.Avatar.Position);
// calc the rotation so the avatar faces the target
Rotation = Helpers.GetRotation(Vector3.Forward, newForward, Vector3.Up);
Cannon.Shoot(Position, Rotation, this);
public static Quaternion GetRotation(Vector3 source, Vector3 dest, Vector3 up)
{
float dot = Vector3.Dot(source, dest);
if (Math.Abs(dot - (-1.0f)) < 0.000001f)
{
// vector a and b point exactly in the opposite direction,
// so it is a 180 degrees turn around the up-axis
return new Quaternion(up, MathHelper.ToRadians(180.0f));
}
if (Math.Abs(dot - (1.0f)) < 0.000001f)
{
// vector a and b point exactly in the same direction
// so we return the identity quaternion
return Quaternion.Identity;
}
float rotAngle = (float)Math.Acos(dot);
Vector3 rotAxis = Vector3.Cross(source, dest);
rotAxis = Vector3.Normalize(rotAxis);
return Quaternion.CreateFromAxisAngle(rotAxis, rotAngle);
}
0*(rotation A) + 1*(rotation B)
- mit anderen Worten, Sie stellen nur die Rotation auf Rotation B auf dem langen Weg ein. Slerp dient nur zum Bestimmen, wie die Drehung zwischen den beiden Elementen aussehen soll (0% <x <100%).