2012-12-28 15 views
5

Sto cercando un'implementazione semplice per la rotazione del arcball su modelli 3D con quaternioni, in particolare utilizzando GLKit su iOS. Finora, ho esaminato le seguenti fonti:Rotazione Arcball con Quaternioni (utilizzando GLKit iOS)

Ho anche cercato di capire il codice sorgente e la matematica da here e here. Posso ruotare il mio oggetto ma continua a saltare intorno ad alcuni angoli, quindi temo che il blocco cardanico sia in gioco. Uso i riconoscitori di gesti per controllare le rotazioni (i gesti della panoramica influiscono su rollio e imbardata, ruotare i gesti sull'intonazione). Allego il mio codice per la gestione del quaternario e la trasformazione della matrice modelview.

Variabili:

  • GLKQuaternion rotationE;

Manipolazione Quaternion:

- (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 (Inside Draw Loop):

// 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); 

Qualsiasi aiuto è molto apprezzato, ma voglio mantenerlo il più semplice possibile e rispettare GLKit.

risposta

5

Sembra che ci siano alcuni problemi in corso qui.

  1. Si dice che si sta usando [x, y] per fare una panoramica, ma sembra più come se li si usasse per l'inclinazione e l'imbardata. Per me, almeno, il panning è la traduzione, non la rotazione.

  2. A meno che non manchi qualcosa, sembra anche che si sostituisca l'intera rotazione ogni volta che si tenta di aggiornarlo. Ruotate un vettore per l'inverso della rotazione corrente e quindi create un quaternione da quel vettore e una certa angolazione. Credo che questo sia equivalente alla creazione del quaternario dal vettore originale e quindi lo ruotiamo per l'inverso della rotazione attuale. Quindi hai q_e'*q_up. Quindi lo moltiplichi con la rotazione corrente, che dà q_e*q_e'*q_up = q_up. La rotazione attuale è annullata. Questo non sembra che sia quello che vuoi.

    Tutto ciò che è veramente necessario fare è creare un nuovo quaterio dall'asse e dall'angolo e quindi moltiplicarlo con il quaternione corrente. Se il nuovo quaternione è a sinistra, la modifica dell'orientamento utilizzerà la cornice occhio-locale. Se il nuovo quaternione è sulla destra, la modifica dell'orientamento sarà nella cornice globale. Penso che si desidera:

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

    fare questo, senza il pre-rotazione inversa per tutti e tre i casi.

  3. Non ho mai usato il GLKit, ma è raro estrarre l'angolo dell'asse quando si converte da Quaternion a Matrix. Se l'angolo è zero, l'asse non è definito. Quando è vicino allo zero, avrai instabilità numerica.Sembra che si dovrebbe utilizzare GLKMatrix4MakeWithQuaternion e poi moltiplicando la matrice risultante con la matrice di traduzione e la matrice di scala:

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

risposta brillante, ha funzionato perfettamente. Questa è la mia prima avventura in quaternioni, quindi la spiegazione chiara è molto apprezzata - grazie! Ho modificato la mia domanda per chiarire il punto sollevato in (1), sebbene il gesto sia denominato "pan" (convenzione iOS), in realtà controlla la rotazione del mio modello. –

3

Mi è stato recentemente chiesto un po 'di più sulla mia conseguente realizzazione di questo problema, così qui è!

- (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)); 
} 

Spero di mettere a buon uso :)

Problemi correlati