Multiplikation
Zumindest im Hinblick auf die Implementierung von Quaternions durch Unity ist die in der Frage beschriebene Multiplikationsreihenfolge nicht korrekt. Dies ist wichtig, da die 3D-Drehung nicht kommutativ ist .
Wenn ich also ein Objekt drehen möchte, indem ich rotationChange
von dem Objekt currentOrientation
ausgehe, würde ich es folgendermaßen schreiben:
Quaternion newOrientation = rotationChange * currentOrientation;
(dh Transformationen stapeln sich bis zur linken Seite - genau wie bei der Unity-Matrixkonvention. Die Drehung ganz rechts wird zuerst / am "lokalsten" Ende angewendet.)
Und wenn ich eine Richtung oder einen Versatzvektor um eine Drehung transformieren wollte, würde ich das so schreiben:
Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;
(Unity erzeugt einen Kompilierungsfehler, wenn Sie das Gegenteil tun.)
Mischen
In den meisten Fällen kommt man mit Lerping-Rotationen davon. Dies liegt daran, dass der Winkel, der "unter der Haube" in einem Quaternion verwendet wird, halb so groß ist wie der Drehwinkel, wodurch er der linearen Annäherung von Lerp wesentlich näher kommt als so etwas wie eine Matrix (die im Allgemeinen nicht gut funktioniert!). Weitere Erklärungen finden Sie in ca. 40 Minuten in diesem Video .
Der einzige Fall, in dem Sie Slerp wirklich brauchen, ist, wenn Sie im Laufe der Zeit eine konstante Geschwindigkeit benötigen, z. B. das Interpolieren zwischen Keyframes auf einer Animationszeitleiste. In Fällen, in denen es Ihnen nur wichtig ist, dass eine Ausgabe zwischen zwei Eingaben liegt (wie das Mischen von Ebenen einer Animation), eignet sich Lerp normalerweise sehr gut.
Was sonst?
Das Skalarprodukt zweier Einheitsquaternionen gibt den Kosinus des Winkels zwischen ihnen an, sodass Sie das Skalarprodukt als Maß für die Ähnlichkeit verwenden können, wenn Sie Rotationen vergleichen müssen. Dies ist allerdings etwas undurchsichtig, sodass ich für besser lesbaren Code häufig Quaternion.Angle (a, b) verwende, was deutlicher ausdrückt, dass wir Winkel in vertrauten Einheiten (Grad) vergleichen.
Diese Arten von praktischen Methoden, die Unity für Quaternions bereitstellt, sind sehr nützlich. In fast jedem Projekt verwende ich dieses mindestens ein paar Mal :
Quaternion.LookRotation(Vector3 forward, Vector3 up)
Dies bildet eine Quaternion, die:
- Dreht die lokale z + -Achse, um genau auf das
forward
Vektorargument zu zeigen
- Dreht die lokale y + -Achse, um so nah wie möglich an das
up
Vektorargument zu zeigen, sofern dies angegeben oder (0, 1, 0)
weggelassen wird
Der Grund, warum das "Auf" nur "so nah wie möglich" kommt, ist, dass das System überbestimmt ist. Wenn forward
wir z + gegenüberstellen, um zwei Freiheitsgrade (dh Gieren und Nicken) zu verbrauchen, bleibt uns nur ein Freiheitsgrad (Rollen).
Ich finde ziemlich oft, dass ich etwas mit den entgegengesetzten Genauigkeitseigenschaften möchte: Ich möchte, dass lokales y + genau entlang up
zeigt und lokales z + forward
mit der verbleibenden Freiheit so nahe wie möglich kommt .
Dies ist zum Beispiel der Fall, wenn Sie versuchen, einen kamerarelativen Koordinatenrahmen für die Bewegungseingabe zu erstellen: Ich möchte, dass meine lokale Aufwärtsrichtung senkrecht zum Boden oder zur Normalen der geneigten Oberfläche bleibt, damit meine Eingabe nicht versucht, den Charakter in das Gelände zu tunneln oder schweben sie davon ab.
Dies kann auch der Fall sein, wenn das Turmgehäuse eines Panzers auf ein Ziel gerichtet sein soll, ohne sich beim Zielen nach oben / unten vom Panzerkörper abzuziehen.
Dazu können wir unsere eigene Komfortfunktion aufbauen, die LookRotation
für das schwere Heben verwendet wird:
Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);
return rotateZToUp * rotateYToZ;
}
Hier drehen wir zuerst das lokale y + zu z + und das lokale z + zu y-.
Dann drehen wir das neue z + in unsere Aufwärtsrichtung (so dass das Nettoergebnis lokal y + zeigt direkt entlang exactUp
) und das neue y + so nah wie möglich an der negierten Vorwärtsrichtung (so dass das Nettoergebnis lokal z + zeigt so nah wie möglich entlang) approximateForward
)
Eine andere praktische Bequemlichkeitsmethode Quaternion.RotateTowards
, die ich oft so benutze:
Quaternion newRotation = Quaternion.RotateTowards(
oldRotation,
targetRotation,
maxDegreesPerSecond * Time.deltaTime
);
Auf diese Weise können wir targetRotation
unabhängig von der Framerate mit einer konstanten, steuerbaren Geschwindigkeit näherkommen - wichtig für Rotationen, die das Ergebnis / die Fairness der Spielmechanik beeinflussen (wie das Drehen der Bewegung eines Charakters oder das Einfahren eines Turmes auf dem Spieler). Naives Lerping / Slerping in dieser Situation kann leicht zu Fällen führen, in denen die Bewegung bei hohen Frameraten schneller wird und die Spielbalance beeinträchtigt. (Das heißt nicht, dass diese Methoden falsch sind - es gibt Möglichkeiten, sie korrekt anzuwenden, ohne die Fairness zu ändern. Sie erfordern lediglich Sorgfalt. Es RotateTowards
gibt eine praktische Verknüpfung, die dies für uns erledigt.)
n
unterschiedliche Ausrichtungen (Einstellungen, Posen usw.). Dann können Sie sie mit Gewichten mitteln und so Slerp / Lerp effektiv verallgemeinern. Sie können ein Quaternion auch in einen Rotor umwandeln, was dem Anwenden einer Winkelgeschwindigkeit für einen bestimmten Zeitraum auf einen starren Körper entspricht. Daher können Sie die Winkelgeschwindigkeitsintegration auch mit Quaternionen beschreiben. Sie können auch schätzen, wie unterschiedlich zwei Ausrichtungen sind (berechnen Sie die Länge des Bogens, der von den beiden Quaternionen auf der Hypersphäre gespannt wird).