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:
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:
- 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.
- 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):
- Drehen Sie zuerst mit einem Quaternion um Y (0, 1, 0).
- Wandle die Rotation Y-Quaternion in eine Matrix um.
- 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.
- 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.World
Parameter für die Rotate
Funktion der Transformation.
Wenn ich Unity verwenden könnte, müsste ich dieses Verhalten leider selbst codieren.
Antworten:
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:
ArcBall Rotation Opengl Tutorial
quelle
Durch Drehen einer akkumulierten Rotationsmatrix konnte ich die erwarteten Rotationen erzielen.
quelle
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.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.
quelle
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.