2012-12-28 5 views
5

Poszukuję prostej implementacji rotacji arcball w modelach 3D z kwaternionami, specjalnie przy użyciu GLKit na iOS. Do tej pory, ja zbadałem następujących źródeł:Rotacja Arcballa z Quaternions (przy użyciu iKlKit iOS)

Ja również stara się zrozumieć kod źródłowy i matematyki z here i here. Mogę obracać mój obiekt, ale wciąż skacze pod pewnymi kątami, więc obawiam się, że blokada kardana jest w grze. Używam rozpoznawania gestów do sterowania obrotami (gesty patrzenia wpływają na przechylenie i odchylenie, gesty obracania wpływają na wysokość tonu). Załączam mój kod do obsługi kwaternionów, a także do transformacji macierzy widoku modelu.

Zmienne:

  • GLKQuaternion rotationE;

Kwaterniony obchodzenia się:

- (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 macierzy transformacji (Wewnątrz Draw pętla)

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

Każda pomoc jest bardzo doceniana, ale chcę ją maksymalnie ograniczyć i trzymać się GLKit.

Odpowiedz

5

Wydaje się, że dzieje się tu kilka problemów.

  1. Mówisz, że używasz [X, Y], aby przesunąć, ale wygląda bardziej jak używasz je rozbić i zboczenia. Dla mnie przynajmniej przesuwanie jest tłumaczeniem, a nie rotacją.

  2. O ile czegoś mi nie brakuje, wygląda na to, że zastępujesz cały obrót za każdym razem, gdy próbujesz go zaktualizować. Obracasz wektor o odwrotność bieżącego obrotu, a następnie tworzysz kwaternion z tego wektora i pod pewnym kątem. Uważam, że jest to równoważne z tworzeniem kwaternionu z oryginalnego wektora, a następnie obracanie go o bieżący obrót odwrotny. Więc masz q_e'*q_up. Następnie pomnóż to przy bieżącym obrocie, co daje q_e*q_e'*q_up = q_up. Bieżąca rotacja zostanie anulowana. To nie wygląda na to, czego chcesz.

    Wszystko, co naprawdę musisz zrobić, to stworzyć nowy kwaternion z osi i kąta, a następnie pomnożyć go z obecnym kwaternionem. Jeśli nowe kwaternion jest po lewej stronie, zmiana orientacji będzie wykorzystywać lokalną ramkę oka. Jeśli nowe kwaternion jest po prawej stronie, zmiana orientacji będzie w ramce globalnej. Myślę, że chcesz:

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

    Wykonaj tę czynność, bez wstępnego obrotu, odwrotnie dla wszystkich trzech przypadków.

  3. Nigdy nie korzystałem z GLKit, ale rzadko się zdarza, aby wyciągać kąt osi podczas konwersji z kwaternionu do macierzy. Jeśli kąt wynosi zero, oś jest niezdefiniowana. Kiedy jest bliski zeru, będziesz miał niestabilność numeryczną.Wygląda na to, należy używać GLKMatrix4MakeWithQuaternion a następnie pomnożenie macierzy wynikowej z matrycą tłumaczeń i matrycy Skala:

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

Brilliant odpowiedź, to działało idealnie. To moje pierwsze przedsięwzięcie na kwaterach, więc jasne wyjaśnienie jest bardzo doceniane - dziękuję! Zmieniłem moje pytanie, aby wyjaśnić swój punkt poruszony w (1), chociaż gest ten nosi nazwę "pan" (konwencja iOS), faktycznie kontroluje rotację mojego modelu. –

3

Byłem niedawno poproszony nieco więcej o moim wynikającej realizacji tego problemu, tak tutaj jest!

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

Nadzieję można umieścić go do dobrego wykorzystania :)

Powiązane problemy