2012-12-28 12 views
5

Ich bin auf der Suche nach einer einfachen Implementierung für Arcball Rotation auf 3D-Modellen mit Quaternionen, speziell mit GLKit auf iOS. Bisher habe ich die folgenden Quellen untersucht:Arcball Rotation mit Quaternions (mit iOS GLKit)

Ich habe auch zu verstehen, Quellcode und Mathematik von here und here versucht. Ich kann mein Objekt drehen, aber es springt in bestimmten Winkeln herum, so dass ich befürchte, dass die kardanische Verriegelung im Spiel ist. Ich benutze Gestenerkenner, um die Rotationen zu steuern (Schwenkgesten beeinflussen Rollen und Gieren, Drehen von Gesten beeinflussen die Tonhöhe). Ich füge meinen Code für die Quaternion-Behandlung sowie die Modelview-Matrix-Transformation an.

Variablen:

  • GLKQuaternion rotationE;

Quaternion Handhabung:

- (void)rotateWithXY:(float)x and:(float)y 
{ 
    const float rate = M_PI/360.0f; 
    GLKVector3 up = GLKVector3Make(0.0f, 1.0f, 0.0f); 
    GLKVector3 right = GLKVector3Make(1.0f, 0.0f, 0.0f); 

    up = GLKQuaternionRotateVector3(GLKQuaternionInvert(self.rotationE), up); 
    self.rotationE = GLKQuaternionMultiply(self.rotationE, GLKQuaternionMakeWithAngleAndVector3Axis(x*rate, up)); 

    right = GLKQuaternionRotateVector3(GLKQuaternionInvert(self.rotationE), right); 
    self.rotationE = GLKQuaternionMultiply(self.rotationE, GLKQuaternionMakeWithAngleAndVector3Axis(y*rate, right)); 
} 

- (void)rotateWithZ:(float)z 
{ 
    GLKVector3 front = GLKVector3Make(0.0f, 0.0f, -1.0f); 

    front = GLKQuaternionRotateVector3(GLKQuaternionInvert(self.rotationE), front); 
    self.rotationE = GLKQuaternionMultiply(self.rotationE, GLKQuaternionMakeWithAngleAndVector3Axis(z, front)); 
} 

Modelview Matrix-Transformation (Looping Unentschieden):

// Get Quaternion Rotation 
GLKVector3 rAxis = GLKQuaternionAxis(self.transformations.rotationE); 
float rAngle = GLKQuaternionAngle(self.transformations.rotationE); 

// Set Modelview Matrix 
GLKMatrix4 modelviewMatrix = GLKMatrix4Identity; 
modelviewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -0.55f); 
modelviewMatrix = GLKMatrix4Rotate(modelviewMatrix, rAngle, rAxis.x, rAxis.y, rAxis.z); 
modelviewMatrix = GLKMatrix4Scale(modelviewMatrix, 0.5f, 0.5f, 0.5f); 
glUniformMatrix4fv(self.sunShader.uModelviewMatrix, 1, 0, modelviewMatrix.m); 

Jede Hilfe wird sehr geschätzt, aber ich möchte es so einfach wie möglich halten und bei GLKit bleiben.

Antwort

5

Es scheint hier ein paar Probleme zu geben.

  1. Sie sagen, dass Sie [x, y] zum Schwenken verwenden, aber es sieht eher so aus, als ob Sie sie zum Pitch und Gieren verwenden. Für mich ist Panning zumindest eine Übersetzung, keine Rotation.

  2. Wenn ich etwas nicht verpasse, sieht es auch so aus, als würde man die gesamte Rotation jedes Mal ersetzen, wenn man versucht, sie zu aktualisieren. Sie drehen einen Vektor um die Umkehrung der aktuellen Drehung und erstellen dann eine Quaternion aus diesem Vektor und einem Winkel. Ich glaube, dass dies äquivalent dazu ist, die Quaternion aus dem ursprünglichen Vektor zu erzeugen und sie dann durch die aktuelle Umkehrung der Rotation zu drehen. So haben Sie q_e'*q_up. Dann multiplizierst du das mit der aktuellen Rotation, was q_e*q_e'*q_up = q_up ergibt. Die aktuelle Rotation wird aufgehoben. Das scheint nicht das zu sein, was du willst.

    Alles, was Sie wirklich tun müssen, ist eine neue Quaternion von Achse und Winkel zu erstellen und dann mit der aktuellen Quaternion zu multiplizieren. Wenn sich die neue Quaternion auf der linken Seite befindet, verwendet die Orientierungsänderung den augenlokalen Rahmen. Wenn die neue Quaternion auf der rechten Seite ist, wird die Orientierungsänderung im globalen Rahmen sein. Ich denke, Sie wollen:

    self.rotationE = 
        GLKQuaternionMultiply( 
        GLKQuaternionMakeWithAngleAndVector3Axis(x*rate, up),self.rotationE); 
    

    Tun Sie dies, ohne die Vor-Rotation durch inverse für alle drei Fälle.

  3. Ich habe das GLKit nie benutzt, aber es ist ungewöhnlich, den Achsenwinkel bei der Konvertierung von Quaternion zu Matrix zu extrahieren. Wenn der Winkel Null ist, ist die Achse nicht definiert. Wenn es nahe Null ist, haben Sie eine numerische Instabilität.Es sieht aus wie Sie GLKMatrix4MakeWithQuaternion verwenden sollen und dann die resultierende Matrix multipliziert mit der Übersetzungsmatrix und Maßstab Matrix:

    GLKMatrix4 modelviewMatrix = 
        GLKMatrix4Multiply(GLKMatrix4MakeTranslation(0.0f, 0.0f, -0.55f), 
             GLKMatrix4MakeWithQuaternion(self.rotationE)); 
    modelviewMatrix = GLKMatrix4Scale(modelviewMatrix, 0.5f, 0.5f, 0.5f); 
    
+0

Brilliant Antwort, es funktionierte perfekt. Dies ist meine erste Unternehmung in Quaternionen, daher wird die klare Erklärung sehr geschätzt - danke! Ich habe meine Frage bearbeitet, um Ihren in (1) angesprochenen Punkt zu verdeutlichen, obwohl die Geste "Pfanne" heißt (iOS-Konvention), sie steuert tatsächlich die Drehung meines Modells. –

3

Ich war vor kurzem ein wenig mehr über meine resultierende Umsetzung dieses Problems gefragt, es so hier ist!

- (void)rotate:(GLKVector3)r 
{ 
    // Convert degrees to radians for maths calculations 
    r.x = GLKMathDegreesToRadians(r.x); 
    r.y = GLKMathDegreesToRadians(r.y); 
    r.z = GLKMathDegreesToRadians(r.z); 

    // Axis Vectors w/ Direction (x=right, y=up, z=front) 
    // In OpenGL, negative z values go "into" the screen. In GLKit, positive z values go "into" the screen. 
    GLKVector3 right = GLKVector3Make(1.0f, 0.0f, 0.0f); 
    GLKVector3 up = GLKVector3Make(0.0f, 1.0f, 0.0f); 
    GLKVector3 front = GLKVector3Make(0.0f, 0.0f, 1.0f); 

    // Quaternion w/ Angle and Vector 
    // Positive angles are counter-clockwise, so convert to negative for a clockwise rotation 
    GLKQuaternion q = GLKQuaternionIdentity; 
    q = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(-r.x, right), q); 
    q = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(-r.y, up), q); 
    q = GLKQuaternionMultiply(GLKQuaternionMakeWithAngleAndVector3Axis(-r.z, front), q); 

    // ModelView Matrix 
    GLKMatrix4 modelViewMatrix = GLKMatrix4Identity; 
    modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, GLKMatrix4MakeWithQuaternion(q)); 
} 

Hoffe, dass Sie es für einen guten Zweck :) setzen