Rotation Arcball avec Quaternions (en utilisant iOS GLKit)

je suis à la recherche d'une implémentation simple pour la rotation arcball sur les modèles 3D avec quaternions, utilisant spécifiquement GLKit sur iOS . Jusqu'à présent, j'ai examiné les sources suivantes:

j'ai aussi essayé de comprendre source code et mathématiques de ici et ici . Je peux faire tourner mon objet mais il continue de sauter à certains angles, donc je crains que Gimbal lock soit en jeu. J'utilise des reconnaisseurs de gestes pour contrôler les rotations (les gestes de pan affectent le roulis et le lacet, les gestes de rotation affectent le tangage). Je joins mon code pour la manipulation du quaternion ainsi que la transformation de la matrice modelview.

Variables:

  • GLKQuaternion rotationE;

Quaternion De La Manipulation:

- (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 De La Matrice De Transformation (À L'Intérieur De Dessiner La Boucle):

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

toute aide est grandement appréciée, mais je veux garder aussi simple que possible et s'en tenir à GLKit.

5
demandé sur Ricardo RendonCepeda 2012-12-29 00:45:32

2 réponses

il semble y avoir quelques problèmes ici.

  1. vous dites que vous utilisez [x,y] pour pan, mais il semble plus comme vous les utilisez pour lancer et lacet. Pour moi, au moins, le laminage est une traduction, Pas une rotation.

  2. à moins que je ne manque quelque chose, on dirait aussi que vous remplacez la rotation entière chaque fois que vous essayez de la mettre à jour. De rotation d'un vecteur par l'inverse de la rotation actuelle et puis créer un quaternion, à partir de ce vecteur et un angle. Je crois que c'est l'équivalent de créer le quaternion à partir du vecteur original et de le faire tourner par l'inverse de la rotation actuelle. Donc vous avez q_e'*q_up . Puis vous multipliez cela avec la rotation actuelle, qui donne q_e*q_e'*q_up = q_up . La rotation actuelle est annulée. Cela ne semble pas comme il est ce que vous voulez.

    Tout ce que vous devez faire est de créer un nouveau quaternion d'axe et d'angle et puis le multiplier avec le courant de quaternions. Si le nouveau quaternion est sur la gauche, le changement d'orientation utilisera le cadre eye-local. Si la nouvelle quaternion est sur la droite, le changement d'orientation sera dans le cadre global. Je pense que vous voulez:

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

    faire ceci, sans la pré-rotation par l'inverse pour les trois cas.

  3. Je n'ai jamais utilisé le GLKit, mais il est rare d'extraire axis-angle lors de la conversion de Quaternion à matrice. Si l'angle est zéro, l'axe n'est pas défini. Quand il sera proche de zéro, vous aurez une instabilité numérique. Il semble que vous devriez utiliser GLKMatrix4MakeWithQuaternion et ensuite multiplier la matrice résultante avec votre matrice de traduction et matrice d'échelle:

    GLKMatrix4 modelviewMatrix = 
      GLKMatrix4Multiply( GLKMatrix4MakeTranslation(0.0f, 0.0f, -0.55f),
                          GLKMatrix4MakeWithQuaternion( self.rotationE ) );
    modelviewMatrix = GLKMatrix4Scale( modelviewMatrix, 0.5f, 0.5f, 0.5f );
    
5
répondu JCooper 2013-01-01 17:08:36

on m'a récemment demandé un peu plus sur ma mise en œuvre résultante de ce problème, alors voilà!

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

Espère vous en faire bon usage :)

3
répondu Ricardo RendonCepeda 2013-09-06 17:28:42