Objekt um feste Achse drehen

12

Ich versuche, dem Benutzer meiner App das Drehen eines in der Mitte des Bildschirms gezeichneten 3D-Objekts zu ermöglichen, indem er seinen Finger auf dem Bildschirm zieht. Eine horizontale Bewegung auf dem Bildschirm bedeutet eine Drehung um eine feste Y-Achse, und eine vertikale Bewegung bedeutet eine Drehung um die X-Achse. Das Problem, das ich habe, ist, dass wenn ich nur eine Drehung um eine Achse zulasse, das Objekt sich gut dreht, aber sobald ich eine zweite Drehung einführe, dreht sich das Objekt nicht wie erwartet.

Hier ist ein Bild von dem, was passiert:

Bildbeschreibung hier eingeben

Die blaue Achse repräsentiert meine feste Achse. Stellen Sie sich den Bildschirm mit dieser festen blauen Achse vor. Ich möchte, dass sich das Objekt in Bezug auf dieses dreht. Was passiert ist in rot.

Folgendes weiß ich:

  1. Die erste Drehung um Y (0, 1, 0) bewirkt, dass sich das Modell vom blauen Feld (dieses Feld als A bezeichnen) in ein anderes Feld (dieses Feld als B bezeichnen) bewegt.
  2. Der Versuch, erneut mit dem Vektor (1, 0, 0) zu drehen, dreht sich um die x-Achse in Raum B NICHT in Raum A, was nicht meine Absicht ist.

Folgendes habe ich versucht, vorausgesetzt, was ich (glaube) weiß (der Kürze halber die W-Koordinate weglassen):

  1. Drehen Sie zuerst mit einem Quaternion um Y (0, 1, 0).
  2. Wandle die Rotation Y-Quaternion in eine Matrix um.
  3. Multiplizieren Sie die Y-Rotationsmatrix mit meiner festen Achse x Vektor (1, 0, 0), um die X-Achse in Bezug auf den neuen Raum zu erhalten.
  4. Drehen Sie sich mit einer Quaternion um diesen neuen X-Vektor.

Hier ist der Code:

private float[] rotationMatrix() {

    final float[] xAxis = {1f, 0f, 0f, 1f};
    final float[] yAxis = {0f, 1f, 0f, 1f};
    float[] rotationY = Quaternion.fromAxisAngle(yAxis, -angleX).toMatrix();

    // multiply x axis by rotationY to put it in object space
    float[] xAxisObjectSpace = new float[4];
    multiplyMV(xAxisObjectSpace, 0, rotationY, 0, xAxis, 0);

    float[] rotationX = Quaternion.fromAxisAngle(xAxisObjectSpace, -angleY).toMatrix();

    float[] rotationMatrix = new float[16];
    multiplyMM(rotationMatrix, 0, rotationY, 0, rotationX, 0);
    return rotationMatrix;
  }

Das funktioniert nicht so, wie ich es erwarte. Die Drehung scheint zu funktionieren, aber irgendwann dreht sich die horizontale Bewegung nicht um die Y-Achse, sondern um die Z-Achse.

Ich bin mir nicht sicher, ob mein Verständnis falsch ist oder ob etwas anderes ein Problem verursacht. Neben der Rotation gibt es noch einige andere Transformationen, die ich an dem Objekt vornehme. Ich bewege das Objekt in die Mitte, bevor ich es drehe. Ich drehe es mit der von meiner obigen Funktion zurückgegebenen Matrix und übersetze es dann -2 in Z-Richtung, damit ich das Objekt sehen kann. Ich glaube nicht, dass dies meine Rotationen durcheinander bringt, aber hier ist der Code dafür:

private float[] getMvpMatrix() {
    // translates the object to where we can see it
    final float[] translationMatrix = new float[16];
    setIdentityM(translationMatrix, 0);
    translateM(translationMatrix, 0, translationMatrix, 0, 0f, 0f, -2);

    float[] rotationMatrix = rotationMatrix();

    // centers the object
    final float[] centeringMatrix = new float[16];
    setIdentityM(centeringMatrix, 0);
    float moveX = (extents.max[0] + extents.min[0]) / 2f;
    float moveY = (extents.max[1] + extents.min[1]) / 2f;
    float moveZ = (extents.max[2] + extents.min[2]) / 2f;
    translateM(centeringMatrix, 0, //
      -moveX, //
      -moveY, //
      -moveZ //
    );

    // apply the translations/rotations
    final float[] modelMatrix = new float[16];
    multiplyMM(modelMatrix, 0, translationMatrix, 0, rotationMatrix, 0);
    multiplyMM(modelMatrix, 0, modelMatrix, 0, centeringMatrix, 0);

    final float[] mvpMatrix = new float[16];
    multiplyMM(mvpMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
    return mvpMatrix;
  }

Ich bin seit ein paar Tagen dabei. Hilfe wird sehr geschätzt.

================================================ ================

AKTUALISIEREN:

Es ist unkompliziert, dies in Unity zum Laufen zu bringen. Hier ist ein Code, der einen am Ursprung zentrierten Würfel dreht:

public class CubeController : MonoBehaviour {

    Vector3 xAxis = new Vector3 (1f, 0f, 0f);
    Vector3 yAxis = new Vector3 (0f, 1f, 0f);

    // Update is called once per frame
    void FixedUpdate () {
        float horizontal = Input.GetAxis ("Horizontal");
        float vertical = Input.GetAxis ("Vertical");

        transform.Rotate (xAxis, vertical, Space.World);
        transform.Rotate (yAxis, -horizontal, Space.World);
    }
}

Der Teil, der bewirkt, dass sich die Rotationen wie erwartet verhalten, ist der Space.WorldParameter für die RotateFunktion der Transformation.

Wenn ich Unity verwenden könnte, müsste ich dieses Verhalten leider selbst codieren.

Christopher Perry
quelle
1
Meine Antwort hier gamedev.stackexchange.com/questions/67199/… könnte Ihnen helfen ..
concept3d
Ich verstehe das Konzept hinter Ihrer Antwort, aber es entgeht mir, wie es umgesetzt werden soll.
Christopher Perry
Wenn Sie die anderen Antworten überprüft haben, implementiert syntac answer die Idee, die ich erklärt habe.
Concept3d
Nein, das ist nicht der Fall, es werden mehrere Umdrehungen um verschiedene Achsen ausgeführt. Sie schlagen eine einzelne Drehung vor.
Christopher Perry

Antworten:

3

Das Problem, das Sie haben, heißt Gimble Lock . Ich denke, was Sie tun wollen, heißt Arcball-Rotation . Die Mathematik für Arcball kann etwas kompliziert sein.

Eine einfachere Möglichkeit besteht darin, einen 2D-Vektor senkrecht zum 2D-Wischen auf dem Bildschirm zu finden.

Nehmen Sie den Vektor und projizieren Sie ihn auf die Kamera in der Nähe des Flugzeugs, um einen 3D-Vektor im Weltraum zu erhalten. Bildschirmraum zum Weltraum .

Dann erstelle eine Quaternion mit diesem Vektor und multipliziere sie mit dem Spielobjekt. Wahrscheinlich mit einigem Schlürfen oder Lerp-Übergang.

Bearbeiten:

Beispiel für die Einheit: Im Beispiel für die Einheit ist der interne Status der Spielobjektrotation eine Quaternion und keine Matrix. Die Methode transform.rotation generiert eine Quaternion basierend auf dem angegebenen Vektor und Winkel und multipliziert diese Quaternion mit der Rotationsquaternion des Spielobjekts. Es wird nur die Rotationsmatrix für das Rendern oder die Physik zu einem späteren Zeitpunkt generiert. Quaternionen sind additiv und vermeiden ein Gimble Lock.

Ihr Code sollte ungefähr so ​​aussehen:

private float[] rotationMatrix() {

    final float[] xAxis = {1f, 0f, 0f, 1f};
    final float[] yAxis = {0f, 1f, 0f, 1f};

    Quaternion qY = Quaternion.fromAxisAngle(yAxis, angleX);
    Quaternion qX = Quaternion.fromAxisAngle(xAxis, -angleY);

    return (qX * qY).getMatrix(); // should probably represent the gameobjects rotation as a quaternion(not a matrix) and multiply all 3 quaternions before generating the matrix. 
  }

ArcBall Rotation Opengl Tutorial

Anthony Raimondo
quelle
Ich erhalte keine Kardansperre, die erste Drehung bewegt die Achse, sodass eine zweite Drehung auf der bewegten Achse basiert. Bitte werfen Sie einen zweiten Blick auf das Bild, das ich zur Verfügung gestellt habe.
Christopher Perry
Ich hoffe du hast es herausgefunden. Zusamenfassend. Quaternionen können zur Rotation multipliziert werden. Sie sollten die Matrix erst am Ende aller Rotationsberechnungen generieren. Auch xQ * yQ ist nicht gleich yQ * xQ. Quaternionen sind nicht kommutativ, wie Christopher Perry sagte.
Anthony Raimondo
Ich habe meinen gesamten Code hier abgelegt . Ich habe das Gefühl, ich habe alles versucht. Vielleicht wird jemand anderes meinen Fehler bemerken.
Christopher Perry
Ich habe es nicht akzeptiert, der Stapelaustausch-Algorithmus hat Ihnen die Punkte automatisch zugewiesen. : /
Christopher Perry
Es tut mir leid für diese Ungerechtigkeit.
Anthony Raimondo
3

Durch Drehen einer akkumulierten Rotationsmatrix konnte ich die erwarteten Rotationen erzielen.

setIdentityM(currentRotation, 0);
rotateM(currentRotation, 0, angleY, 0, 1, 0);
rotateM(currentRotation, 0, angleX, 1, 0, 0);

// Multiply the current rotation by the accumulated rotation,
// and then set the accumulated rotation to the result.
multiplyMM(temporaryMatrix, 0, currentRotation, 0, accumulatedRotation, 0);
arraycopy(temporaryMatrix, 0, accumulatedRotation, 0, 16);
Christopher Perry
quelle
1

Ihr Bild entspricht Ihrem rotationMatrix-Code. Durch Drehen der x-Achse mit Ihrer vorherigen y-Drehung erhalten Sie die lokale x-Achse. Wenn Sie dann das Objekt umdrehen, erhalten Sie das Ergebnis, das Sie in Ihrem Bild anzeigen. Damit die Drehung aus Sicht des Benutzers logisch ist, möchten Sie das Objekt stattdessen mit der Weltkoordinatenachse drehen.

Wenn Sie möchten, dass Ihr Benutzer Ihr Objekt mehrmals drehen kann, ist es sinnvoll, Ihre Drehung in einer Quaternion anstelle einer Matrix zu speichern. Mit der Zeit führen mehrere Drehungen (und Gleitkommaungenauigkeiten) dazu, dass die Matrix immer weniger aussieht eine Rotationsmatrix, das gleiche passiert natürlich in einer Quaternion, aber nur das Normalisieren der Quaternion bringt sie zu einer guten Rotation zurück.

Verwenden Sie einfach die Identitätsquaternion als Anfangswert, und jedes Mal, wenn der Benutzer über den Bildschirm wischt, wird die Quaternion mit dem Quaternion.fromAxisAngle(yAxis, -angleX)Code gedreht. Verwenden Sie immer (1,0,0,1) für x-Umdrehungen und (0,1,0,1) für y-Umdrehungen.

static final float[] xAxis = {1f, 0f, 0f, 1f};
static final float[] yAxis = {0f, 1f, 0f, 1f};

private void rotateObject(float angleX, float angleY) {
  Quaternion rotationY = Quaternion.fromAxisAngle(yAxis, -angleX);
  Quaternion rotationX = Quaternion.fromAxisAngle(xAxis, -angleY);

  myRotation = myRotation.rotate(rotationY).rotate(rotationX).normalize();
}
private float[] rotationMatrix() {
  return myRotation.toMatrix();
}

Da Sie die Sprache oder ein bestimmtes Framework nicht erwähnt haben, werden die Methoden in Quaternion möglicherweise anders genannt, und normalize ist nicht erforderlich, um dies häufig aufzurufen verlangsamen die Dinge viel und auf diese Weise besteht keine Chance, dass die Quaternion von einer Einheitsquaternion abrutscht.

Daniel Carlsson
quelle
Ich bekomme genau das gleiche Verhalten dabei.
Christopher Perry
1
@ChristopherPerry Versuchen Sie, die Rotationsreihenfolge umzukehren: myRotation = rotationX.rotate(rotationY).rotate(myRotation).normalize()Sie sind nicht kommutativ, sodass die Reihenfolge, in der Sie sie ausführen, zählt. Fügen Sie einen weiteren Kommentar mit Ihrem Framework / Ihrer Sprache hinzu, wenn dies nicht funktioniert hat, und ich werde mich ein bisschen mehr damit befassen.
Daniel Carlsson
Das geht auch nicht, ich bekomme das gleiche Verhalten.
Christopher Perry
Ich habe meinen gesamten Code hier
Christopher Perry