Slerping Rotationsspiegel

9

Ich drehe meinen Spielcharakter, um das Ziel mit dem folgenden Code zu beobachten:

        transform.rotation = Quaternion.Slerp(startQuaternion, lookQuaternion, turningNormalizer*turningSpeed/10f)

startQuaternion ist die aktuelle Rotation des Charakters, wenn ein neues Ziel angegeben wird.

lookQuaternion ist die Richtung, in die der Charakter schauen soll, und sie ist wie folgt eingestellt:

    destinationVector = currentWaypoint.transform.position - transform.position;
    lookQuaternion = Quaternion.LookRotation(destinationVector, Vector3.up);

turnNormalizer wird nur Time.deltaTimeinkrementiert und turningSpeedist ein statischer Wert, der im Editor angegeben wird.

Das Problem ist, dass sich der Charakter zwar die meiste Zeit so dreht, wie er sollte, aber Probleme hat, wenn er sich in der Nähe von 180 Grad bewegen muss. Dann zittert und spiegelt es manchmal die Rotation:

Geben Sie hier die Bildbeschreibung ein

In diesem schlecht gezeichneten Bild beginnt sich das Zeichen (rechts) in Richtung des Kreises links zu drehen. Anstatt sich nur nach links oder rechts zu drehen, beginnt dieser "Spiegeltanz":

  1. Es beginnt sich in Richtung der neuen Verkleidung zu drehen
  2. Dann schnappt es plötzlich in den gleichen Winkel, aber auf der anderen Seite, und dreht sich weiter

Diese "Spiegelung" wird so lange durchgeführt, bis das Ziel angezeigt wird. Ist das eine Sache mit Quaternionen, Slerping / Lerping oder etwas anderem?

EDIT1: Anscheinend entsteht das Problem nicht durch das Drehen selbst. Das wahrscheinlichere Problem ist, dass sich der Charakter während der Drehung in Richtung der Vorderseite bewegt. Durch die Begrenzung des Winkels zwischen Ausrichtung und Ziel wird die Jitter- / Spiegelrotation verringert und zeitweise beseitigt, wenn sich der Charakter bewegen darf.

Dies wirft natürlich weitere Fragen auf: Warum kann sich der Charakter nicht ohne Probleme gleichzeitig bewegen und drehen? Für die Bewegung verwendeter Code:

transform.Translate(Vector3.forward * runningSpeed/10f * Time.deltaTime);
Esa
quelle
Wird 'lookQuaternion' in jedem Frame aktualisiert? Wenn ja, dann würde ich davon ausgehen, dass einige kleine Ungenauigkeiten zu einer Art Jitter führen, bei dem der Spieler die Look-Rotation "überschreitet" und die neue Blickrichtung genau auf der anderen Seite von "direkt dahinter" liegt ... .
Steven Stadnicki

Antworten:

5

Jede Ausrichtung im 3D-Raum kann durch zwei unterschiedliche Einheitsquaternionen dargestellt qund -q(komponentenweise negiert q) werden. Zum Beispiel kann die durch die 3x3-Identitätsmatrix dargestellte Orientierung Idurch 2 Quaternionen dargestellt werden:

 q:  { 0,  0,  0},  1
-q:  {-0, -0, -0}, -1

Beide repräsentieren die gleiche Ausrichtung im 3D-Raum, ihr Punktprodukt liegt genau -1auf der anderen Hemisphäre, genau auf den gegenüberliegenden Seiten der Hypersphäre.

Das Ergebnis, das Sie beobachten, wenn der Slerp den längeren Bogen benötigt, um sich zu drehen, ist das Slerpen zwischen zwei Quaternionen, die nicht auf derselben Hemisphäre liegen. Wenn dies passiert, negiere einfach einen von ihnen, bevor du sie slerpst, dann nimmt der Slerp den kürzeren Bogen.

Das Punktprodukt ist ein einfaches Werkzeug, um herauszufinden, ob dies geschieht. Wenn das Punktprodukt beider Quaternionen kleiner als ist 0, liegen sie nicht auf derselben Halbkugel. Wenn also das Punktprodukt von beiden kleiner als ist 0, negieren Sie einfach komponentenweise die andere Quaternion, bevor Sie sie slerpieren.

Maik Semder
quelle
Ich frage mich, ob ich Sie richtig verstanden habe, als ich es damit versucht habe. if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Inverse(startQuaternion); }Aber es hat das Problem nicht behoben. Entschuldigung für die schlechte Formatierung, ich kann diesen Kommentarbereich nicht zähmen.
Esa
Fast, aber nicht Inverse, das ist eine andere Operation als Negate. if(Quaternion.Dot (lookQuaternion, q) < 0) { q.x = -q.x; q.y = -q.y; q.z = -q.z; q.w = -q.w; }Ich benutze kein C #, aber nach dem Aussehen des Dokuments scheint es, dass Sie die Negate-Funktion auch direkt verwenden können.
Maik Semder
if(Quaternion.Dot (lookQuaternion, startQuaternion) < 0) { startQuaternion = Quaternion.Negate(startQuaternion); }
Maik Semder
Ich fürchte, diese haben das Problem nicht behoben. Ich habe die Frage aktualisiert.
Esa
Ja, das ist wahrscheinlich eine Mischung aus verschiedenen Fehlern. Vereinfachen Sie Ihren Testaufbau, testen Sie jeweils eine Sache und nicht gleichzeitig Translation und Rotation. Stellen Sie zuerst sicher, dass eines funktioniert, dann das andere und dann beide zusammen. Konzentrieren Sie sich zuerst nur auf die Rotation, verwenden Sie einige bekannte Orientierungswerte, z. B. beginnen Sie bei 170 Grad, bewegen Sie sich auf Null, sehen Sie, wo es schief geht, und veröffentlichen Sie es hier
Maik Semder
1

Nun, Ihr Problem ist, dass diese beiden Vektoren, die Ihre Quaternionen bilden, 180 Grad bilden. Die Frage, die bei der Interpolation gestellt werden sollte, welchen Bogen soll er nehmen? der obere Bogen oder der untere?

Dies ist der Grund, warum die Spiegelung jedes Mal, wenn Sie interpolieren, umgekehrt erfolgt, während der Winkel beibehalten wird.

Nach diesem Link .

Wenn Theta 180 Grad beträgt, ist das Ergebnis undefiniert, da es keine kürzeste Drehrichtung gibt. Ich denke, diese Situation wird noch nicht richtig gehandhabt. Es wäre besser, wenn wir eine beliebige Achse wählen, die normal zu qa oder qb ist schätzen alle Ideen für den besten Weg, dies zu tun.

Was Sie tun müssen, ist, Ihre Startquaternion mit einer Zwischenquaternion zu interpolieren (um zu bestimmen, welcher Bogen genommen werden soll) und bei Erreichen die Zwischenquaternion zu verwenden, um mit der tatsächlichen Zielquaternion fortzufahren.

concept3d
quelle
1

Das Problem, von dem ich vermute, dass es angezeigt wird, hat nichts mit Ihrem Code zu tun, sondern ist ein grundlegendes Problem bei der Interpolation entlang der Oberfläche einer Kugel (in diesem Fall für die viewDirection). Zwischen (fast) zwei beliebigen Punkten auf einer Kugel befindet sich ein einzelner Großkreis. Wenn Sie beispielsweise unseren Startpunkt willkürlich am Nordpol festlegen, ist der Großkreis der Meridian, auf dem der Endpunkt liegt. Die Interpolation von einem Punkt zum anderen bewegt sich entlang dieses großen Kreises.

Für die meisten Punkte auf einer Kugel entsprechen nahegelegene Punkte nahegelegenen Großkreisen. Zum Beispiel sind die Meridiane, auf denen Los Angeles und Phoenix liegen, ziemlich nahe beieinander. Wenn der Zielpunkt jedoch der Antipode des ursprünglichen Punkts ist - der Südpol zum Nordpol des Startpunkts -, gibt es nicht mehr einen einzigen großen Kreis durch beide, sondern alle großen Kreise durch einen gehen durch den anderen. Was noch schlimmer ist, dies bedeutet, dass Punkte in der Nähe des Südpols nicht die meisten Punkte sind. Zwei Punkte in der Nähe und beide in der Nähe des Südpols können sehr unterschiedliche Meridiane aufweisen.

Ich vermute, dass das, was Sie sehen, eine Manifestation dieser Instabilität im Meridian ist - oder mit anderen Worten, im Interpolationsbogen. Wenn sich das Look-Ziel direkt hinter dem Charakter befindet, werden Dinge wie die Ungenauigkeit erster Ordnung in Ihrer 'Euler-Integration' der Position des Charakters und die Art und Weise, wie sich die Blickrichtung von Frame zu Frame ändert, zu diesen wilden Folgen führen Unterschiedliche Interpolationsbögen von einem Bild zum nächsten, und dies führt wahrscheinlich zu der "Staffelung", die Sie sehen.

Was damit zu tun ist, ist das Beste, was mir in den Sinn kommt, die Blickrichtung des Ziels nicht in jedem Bild neu zu berechnen. Verwenden Sie stattdessen dasselbe für einen Zeitraum von mehreren Frames, berechnen Sie dann einen neuen und verwenden Sie diesen für mehrere Frames usw. Bei Bedarf können Sie sogar über einen Zeitraum von wenigen Frames von einem Ziel zum nächsten interpolieren, sodass Die Bewegungsrichtung ändert sich nicht zu abrupt.

Steven Stadnicki
quelle