2013-05-05 11 views
6

Voglio codificare una videocamera in prima persona con la sua rotazione memorizzata in un quaternion. Sfortunatamente c'è qualcosa di sbagliato nella rotazione.Come ruotare correttamente un quaternione lungo tutti gli assi?

La seguente funzione è responsabile di ruotare la telecamera. I parametri Mouse e Speed passano il movimento del mouse e la velocità di rotazione. Quindi la funzione recupera il quaternario di rotazione, lo ruota e memorizza il risultato. A proposito, sto usando Bullet Physics da cui provengono i tipi e le funzioni.

void Rotate(vec2 Mouse, float Speed) 
{ 
    btTransform transform = camera->getWorldTransform(); 
    btQuaternion rotation = transform.getRotation(); 

    Mouse = Mouse * Speed;     // apply mouse sensitivity 
    btQuaternion change(Mouse.y, Mouse.x, 0); // create quaternion from angles 
    rotation = change * rotation;    // rotate camera by that 

    transform.setRotation(rotation); 
    camera->setWorldTransform(transform); 
} 

per illustrare la rotazione della telecamera risultante quando il mouse si muove, ti mostrano un disegno a mano. Sul lato sinistro viene mostrata la rotazione errata che la telecamera effettivamente esegue. Sul lato destro è mostrato il caso corretto desiderato. Le frecce indicano come ruota la fotocamera quando si sposta il mouse verso l'alto (in arancione) e verso il basso (in blu).

wrong rotation on the left and desired rotation on the right

Come si può vedere, fino a quando l'imbardata è pari a zero, la rotazione è corretto. Ma più l'imbardata ha, più piccoli diventano i cerchi in cui ruota la telecamera. Al contrario, i cerchi dovrebbero sempre percorrere l'intera sfera come una longitudine.

Non ho molta familiarità con i quaternioni, quindi qui vi chiedo come ruotarli correttamente.

+0

Le coordinate del mouse si trovano nello spazio dello schermo molto probabilmente, il che significa che non è praticamente una buona idea usarle in questo modo. Invece, convertirli in coordinate del mondo usando la matrice di proiezione inversa. – BlackCat

+0

La mia idea era di usare le coordinate del mouse 'x' e' y' come angoli per ruotare il quaternione della telecamera attorno al vettore 'su' e' destra'. Penso che sia fatto così nella maggior parte dei giochi. Non sono sicuro che il tuo approccio funzioni. Potresti spiegare un po 'oltre? – danijar

risposta

9

Ho scoperto come ruotare correttamente un quaterio per conto mio. La chiave era trovare i vettori per l'asse che voglio ruotare intorno. Quelli sono usati per creare quaternioni dall'asse e dall'angolo, quando l'angolo è la quantità da ruotare attorno all'asse reale.

Il seguente codice mostra cosa ho ottenuto. Permette anche di rotolare la fotocamera, che potrebbe essere utile un po 'di tempo.

void Rotate(btVector3 Amount, float Sensitivity) 
{ 
    // fetch current rotation 
    btTransform transform = camera->getWorldTransform(); 
    btQuaternion rotation = transform.getRotation(); 

    // apply mouse sensitivity 
    Amount *= Sensitivity; 

    // create orientation vectors 
    btVector3 up(0, 1, 0); 
    btVector3 lookat = quatRotate(rotation, btVector3(0, 0, 1)); 
    btVector3 forward = btVector3(lookat.getX(), 0, lookat.getZ()).normalize(); 
    btVector3 side = btCross(up, forward); 

    // rotate camera with quaternions created from axis and angle 
    rotation = btQuaternion(up,  Amount.getY()) * rotation; 
    rotation = btQuaternion(side, Amount.getX()) * rotation; 
    rotation = btQuaternion(forward, Amount.getZ()) * rotation; 

    // set new rotation 
    transform.setRotation(rotation); 
    camera->setWorldTransform(transform); 
} 

Da quando ho raramente trovato informazioni sulla rotazione quaternione, passerò un po 'di tempo per spiegare ulteriormente il codice di cui sopra.

Il recupero e l'impostazione della rotazione sono specifici del motore fisico e non sono correlati a questa domanda, quindi non lo spiegherò. La parte successiva, moltiplicando la quantità per una sensibilità del mouse dovrebbe essere molto chiara. Continuiamo con i vettori di direzione.

  • Il vettore up dipende dalla propria implementazione. Più convenientemente, l'asse Y positivo punta verso l'alto, quindi finiamo con 0, 1, 0.
  • Il vettore lookat rappresenta la direzione in cui la fotocamera guarda. Semplicemente ruotiamo un vettore unitario puntato in avanti dal quaternione di rotazione della telecamera. Di nuovo, il vettore di puntamento in avanti dipende dalle convenzioni. Se l'asse Y è sollevato, l'asse Z positivo potrebbe puntare in avanti, ovvero 0, 0, 1.
  • Non confonderlo con il vettore successivo. Si chiama forward che fa riferimento alla rotazione della telecamera. Pertanto, abbiamo solo bisogno di proiettare il vettore lookat a terra. In questo caso, prendiamo semplicemente il vettore lookat e ignoriamo il componente di puntamento verso l'alto. Per la pulizia normalizziamo quel vettore.
  • I punti vettore side verso sinistra dall'orientamento della telecamera. Pertanto si trova perpendicolare sia al up sia al vettore forward e possiamo usare lo cross product per calcolarlo.

Dato questi vettori, possiamo ruotare correttamente il quaternione della fotocamera attorno ad essi. Quale che inizi, Z, Y o Z, dipende dallo Euler angle sequence che è, ancora una volta, una convenzione che varia da un'applicazione all'altra. Dato che voglio applicare le rotazioni nell'ordine Y X Z, faccio quanto segue.

  • Innanzitutto, ruotare la telecamera attorno all'asse up in base alla quantità per la rotazione Y. Questo è imbardata.
  • Quindi ruotare attorno all'asse side, che punta verso sinistra, per l'importo X. È pece.
  • Infine, ruotare attorno al vettore forward in base alla quantità Z per applicare il rotolo.

Per applicare tali rotazioni, è necessario moltiplicare i quaternioni creati per asse e angolo con la rotazione della telecamera corrente. Infine applichiamo il quaternio ottenuto al corpo nella simulazione fisica.

0

Matrici e beccheggio/imbardata/rollio entrambi con i loro limiti, non li uso più ma uso quaternioni invece. Ruotino il vettore vista e ricalcolo prima i vettori della telecamera, poi la matrice della vista rispetto al vettore vista ruotato.

void Camera::rotateViewVector(glm::quat quat) { 

    glm::quat rotatedViewQuat; 

    quat = glm::normalize(quat); 
    m_viewVector = glm::normalize(m_viewVector); 

    glm::quat viewQuat(0.0f, 
     m_viewVector.x, 
     m_viewVector.y, 
     m_viewVector.z); 

    viewQuat = glm::normalize(viewQuat); 

    rotatedViewQuat = (quat * viewQuat) * glm::conjugate(quat); 
    rotatedViewQuat = glm::normalize(rotatedViewQuat); 

    m_viewVector = glm::normalize(glm::vec3(rotatedViewQuat.x, rotatedViewQuat.y, rotatedViewQuat.z)); 
    m_rightVector = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), m_viewVector)); 
    m_upVector = glm::normalize(glm::cross(m_viewVector, m_rightVector)); 
} 
Problemi correlati