Come ruotare un oggetto attorno agli assi allineati al mondo?


15

Ho un Vector3 che ha un angolo di eulero per ciascun asse.

Di solito, quando voglio creare una matrice di rotazione, userò funzioni come D3DXMatrixRotationX passando il rispettivo angolo dal mio vettore di rotazione sopra e moltiplicando le matrici (ZXY) per creare la matrice di rotazione complessiva che viene utilizzata per formare la matrice di trasformazione completa dell'oggetto.

Tuttavia, questo metodo produrrà una serie di rotazioni nello spazio oggetti. Cioè, passare un vettore di (90, 0, 90) nel mio metodo creerà una rotazione nello spazio mondiale efficacemente (90, 90, 0).

C'è un modo per garantire sempre che ogni componente del mio vettore di rotazione dia luogo a una rotazione attorno ai rispettivi assi allineati nello spazio mondiale?

MODIFICARE:

Questa è un'animazione di ciò che sta accadendo attualmente: voglio un modo per ruotare attorno agli assi blu, non al rosso.

Euler Angles

MODIFICA 2:

Solo per notare che non sto cercando una soluzione che coinvolga gli angoli di Eulero, ma semplicemente un modo in cui posso rappresentare una trasformazione di più rotazioni attorno agli assi del mondo.


Cosa c'è di sbagliato nel chiamare tre volte le funzioni di differnet e nel filtrare le parti dei vettori che non si desidera (impostandole su 0 prima di chiamare la funzione)? Altrimenti non sono sicuro di ciò che stai cercando di ottenere.
TravisG,

Stai filtrando cosa? Chiamo le 3 funzioni separate e quindi le moltiplico per creare la matrice di trasformazione. Questo però archivia una rotazione locale.
Syntac_

Vuoi angoli di Eulero o rotazione attorno agli assi del mondo? Si noti che dalla definizione degli angoli di Eulero (ad es. En.wikipedia.org/wiki/Euler_angles ), solo l'angolo alfa è strettamente attorno ad un asse mondiale. Gli altri due angoli sono relativi agli assi inclinati che non coincidono necessariamente con gli assi del mondo.
DMGregory

1
Usando gli angoli di Eulero stai moltiplicando tutte e tre le matrici di rotazione prima di applicare poi sul vertice. Se M, N, O sono le matrici di rotazione, l'operazione risultante è MNO v. Quello che ho proposto è di applicare ciascuna matrice separatamente: v1 = O v0, quindi v2 = N v1 e infine v3 = M v2. In questo modo ogni vi sarà nelle coordinate del mondo e dovrai solo usare una matrice di rotazione per l'asse corrente anche nelle coordinate del mondo.
dsilva.vinicius,

3
@ dsilva.vinicius Le tue trasformazioni separate sono esattamente le stesse di quelle combinate, o per dirla in un altro modo: MNO v == M * (N * (O v))
GuyRT

Risposte:


1

Sulla base dei tuoi commenti, sembra che tu stia memorizzando l'orientamento dell'oggetto come un insieme di angoli di Eulero e in / diminuendo gli angoli quando il giocatore ruota l'oggetto. Cioè, hai qualcosa di simile a questo pseudocodice:

// in player input handling:
if (axis == AXIS_X) object.angleX += dir;
else if (axis == AXIS_Y) object.angleY += dir;
else if (axis == AXIS_Z) object.angleZ += dir;

// in physics update and/or draw code:
matrix = eulerAnglesToMatrix(object.angleX, object.angleY, object.angleZ);

Come osserva Charles Beattie , poiché le rotazioni non commutano, questo non funzionerà come previsto a meno che il giocatore non ruoti l'oggetto nello stesso ordine in cui si eulerAnglesToMatrix()applicano le rotazioni.

In particolare, considerare la seguente sequenza di rotazioni:

  1. ruotare l'oggetto di x gradi attorno all'asse X;
  2. ruotare l'oggetto di y gradi attorno all'asse Y;
  3. ruota l'oggetto di - x gradi attorno all'asse X;
  4. ruota l'oggetto di - y gradi attorno all'asse Y.

Nella rappresentazione dell'angolo di Eulero ingenuo, come implementato nello pseudocodice sopra, queste rotazioni verranno annullate e l'oggetto tornerà al suo orientamento originale. Nel mondo reale, ciò non accade: se non mi credi, prendi un dado a sei facce o un cubo di Rubik, lascia che x = y = 90 ° e provalo tu stesso!

La soluzione, come si nota nella propria risposta , è quella di memorizzare l'orientamento dell'oggetto come matrice di rotazione (o quaternione) e aggiornare quella matrice in base all'input dell'utente. Cioè, invece dello pseudocodice sopra, faresti qualcosa del genere:

// in player input handling:
if (axis == AXIS_X) object.orientation *= eulerAnglesToMatrix(dir, 0, 0);
else if (axis == AXIS_Y) object.orientation *= eulerAnglesToMatrix(0, dir, 0);
else if (axis == AXIS_Z) object.orientation *= eulerAnglesToMatrix(0, 0, dir);

// in physics update and/or draw code:
matrix = object.orientation;  // already in matrix form!

(Tecnicamente, poiché qualsiasi matrice di rotazione o quaternione può essere rappresentata come un insieme di angoli di Eulero, è possibile usarli per memorizzare l'orientamento dell'oggetto. Ma la regola fisicamente corretta per combinare due rotazioni sequenziali, ciascuna rappresentata come angoli di Eulero, in una singola rotazione è piuttosto complicato, e sostanzialmente equivale a convertire le rotazioni in matrici / quaternioni, moltiplicandole e poi riconvertire il risultato in angoli di Eulero.)


Sì, hai ragione, questa era la soluzione. Penso che questo sia leggermente migliore della risposta di concept3d poiché dà l'impressione che sia necessario un quaternione, ma non è vero. Finché ho memorizzato la rotazione corrente come una matrice e non i tre angoli di Eulero, andava bene.
Syntac_

15

Il problema con le rotazioni è che la maggior parte delle persone lo pensa in termini di angoli di Eulero, poiché sono facili da capire.

Eppure la maggior parte delle persone dimentica il punto che gli angoli di Eulero sono tre angoli sequenziali . Ciò significa che la rotazione attorno al primo asse renderà la rotazione successiva relativa alla prima rotazione originale, quindi non è possibile ruotare in modo indipendente un vettore attorno a ciascuno dei 3 assi usando gli angoli di Eulero.

Questo si traduce direttamente in matrici quando moltiplichi due matrici, puoi pensare a questa moltiplicazione come trasformazione di una matrice nello spazio dell'altra matrice.

Questo dovrebbe accadere con 3 rotazioni sequenziali anche quando si usano i quaternioni.

inserisci qui la descrizione dell'immagine

Voglio sottolineare il fatto che i quaternioni non sono una soluzione per il blocco dei gimble. In realtà il blocco della gimble avverrà sempre se rappresentassi gli angoli di Eulero usando i quaternioni. Il problema non è la rappresentazione, il problema sono i 3 passaggi sequenziali.

La soluzione?

La soluzione per ruotare un vettore attorno a 3 assi in modo indipendente è quella di combinare in un singolo asse e un singolo angolo, in questo modo è possibile eliminare il passaggio in cui è necessario eseguire una moltiplicazione sequenziale. Questo si tradurrà efficacemente in:

La mia matrice di rotazione rappresenta il risultato della rotazione attorno a X, Y e Z.

piuttosto che l'interpretazione di Eulero di

La mia matrice di rotazione rappresenta la rotazione attorno a X, quindi a Y e poi a Z.

Per chiarire questo citerò dal teorema di rotazione di Eulero di Wikipedia:

Secondo il teorema di rotazione di Eulero, qualsiasi rotazione o sequenza di rotazioni di un corpo rigido o di un sistema di coordinate attorno a un punto fisso equivale a una singola rotazione di un dato angolo θ attorno ad un asse fisso (chiamato asse di Eulero) che attraversa il punto fisso. L'asse di Eulero è in genere rappresentato da un vettore unitario u →. Pertanto, qualsiasi rotazione in tre dimensioni può essere rappresentata come una combinazione di un vettore u → e uno scalare θ. I quaternioni forniscono un modo semplice per codificare questa rappresentazione dell'asse-asse in quattro numeri e applicare la rotazione corrispondente a un vettore di posizione che rappresenta un punto relativo all'origine in R3.

Notare che la moltiplicazione di 3 matrici rappresenterà sempre 3 rotazioni sequenziali.

Ora in ordine per combinare rotazioni attorno a 3 assi, è necessario ottenere un singolo asse e singoli angoli che rappresentano la rotazione attorno a X, Y, Z. In altre parole, è necessario utilizzare una rappresentazione Asse / Angolo o Quaternione per eliminare le rotazioni sequenziali.

Questo di solito viene fatto, iniziando con un orientamento iniziale (l'orientamento può essere pensato come un angolo dell'asse), solitamente rappresentato come un quaternione o un angolo dell'asse, e quindi modificando quell'orazione per rappresentare l'orientamento della destinazione. Ad esempio, si inizia con il quaterion identità e quindi si ruota dalla differenza per raggiungere l'orientamento della destinazione. In questo modo non perdi alcun grado di libertà.


Contrassegnato come risposta in quanto sembra penetrante.
Syntac_13

Ho dei problemi a capire cosa stai cercando di dire con questa risposta. È semplicemente "non memorizzare l'orientamento di un oggetto come angoli di Eulero"? E se è così, perché non dirlo?
Ilmari Karonen,

@IlmariKaronen Si potrebbe dire più chiaramente, ma penso che concept3d stia promuovendo la rappresentazione dell'angolo dell'asse; vedere la sezione 1.2.2 di questo documento per la relazione tra asse-angolo e quaternioni. La rappresentazione dell'angolo dell'asse è più facile da implementare per i motivi di cui sopra, non soffre di gimbal-lock e (almeno per me) è altrettanto facile da capire quanto gli angoli di Eulero.
NauticalMile,

@ concept3d, è molto interessante e mi piace molto la tua risposta. Manca comunque una cosa, le persone interagiscono con il computer usando una tastiera e un mouse, se pensiamo al mouse allora parliamo di delta xey. Come rappresentare questi delta x, y con un singolo quaternione che possiamo usare per generare la matrice di rotazione, ad esempio per cambiare l'orientamento di un oggetto?
gmagno,

@gmagno l'approccio è di solito di proiettare il movimento del mouse sugli oggetti o sulla scena e calcolare i delta in quello spazio, lo fai lanciando un raggio e calcolando l'intersezione. Cerca ray casting, proietta e non proietta, sono approssimativo sui dettagli poiché non lavoro in CG da anni. Spero possa aiutare.
concept3d

2

Cambiare una combinazione di rotazioni dallo spazio degli oggetti allo spazio del mondo è banale: devi solo invertire l'ordine in cui vengono applicate le rotazioni.

Nel tuo caso, invece di moltiplicare le matrici Z × X × Y, devi solo calcolare Y × X × Z.

Una logica per questo può essere trovata su Wikipedia: conversione tra rotazioni intrinseche ed estrinseche .


Se ciò fosse vero, la seguente affermazione della tua fonte non sarebbe vera, perché le rotazioni sarebbero diverse: "Qualsiasi rotazione estrinseca equivale a una rotazione intrinseca con gli stessi angoli ma con ordine inverso di rotazioni elementali e viceversa ".
Syntac_

1
Non vedo alcuna contraddizione qui. Sia la mia risposta che quella affermazione sono vere. E sì, eseguire rotazioni nello spazio degli oggetti e nello spazio del mondo produce rotazioni diverse; questo è esattamente il punto, no?
Sam Hocevar,

Tale affermazione afferma che la modifica dell'ordine comporterà sempre la stessa rotazione. Se un ordine produce una rotazione errata, anche l'altro ordine significa che non è una soluzione.
Syntac_

1
Stai fraintendendo. La modifica dell'ordine non comporta la stessa rotazione. Cambiando l'ordine e passando da rotazioni intrinseche a rotazioni estrinseche si ottiene la stessa rotazione.
Sam Hocevar,

1
Non credo di aver capito la tua domanda. La GIF mostra una rotazione di circa 50 gradi intorno Z(spazio oggetto), quindi 50 gradi intorno X(spazio oggetto), quindi 45 gradi intorno Y(spazio oggetto). Questo è esattamente lo stesso di una rotazione di 45 gradi intorno Y( spazio del mondo ), quindi di 50 gradi intorno X( spazio del mondo ), quindi di 50 gradi intorno Z( spazio del mondo ).
Sam Hocevar,

1

Fornirò la mia soluzione come risposta fino a quando qualcuno non può spiegare perché funziona.

Ogni render stavo ricostruendo il mio quaternione usando gli angoli memorizzati nel mio vettore di rotazione e quindi applicando il quaternione alla mia trasformazione finale.

Tuttavia, al fine di tenerlo attorno agli assi del mondo, ho dovuto trattenere il quaternione su tutti i fotogrammi e ruotare solo gli oggetti usando una differenza di angolo, cioè ..

// To rotate an angle around X - note this is an additional rotation.
// If currently rotated 90, apply this function with angle of 90, total rotation = 180.
D3DXQUATERNION q;
D3DXQuaternionRotation(&q, D3DXVECTOR3(1.0f, 0.0f, 0.0f), fAngle);
m_qRotation *= q; 

//...

// When rendering rebuild world matrix
D3DXMATRIX mTemp;
D3DXMatrixIdentity(&m_mWorld);

// Scale
D3DXMatrixScaling(&mTemp, m_vScale.x, m_vScale.y, m_vScale.z);
m_mWorld *= mTemp;

// Rotate
D3DXMatrixRotationQuaternion(&mTemp, m_qRotation);
m_mWorld *= mTemp;

// Translation
D3DXMatrixTranslation(&mTemp, m_vPosition.x, m_vPosition.y, m_vPosition.z);
m_mWorld *= mTemp;

(Dettagliato per la leggibilità)

Penso che dsilva.vinicius stesse cercando di arrivare a questo punto.


1

Dovrai memorizzare l'ordine delle rotazioni.

Rotating around x 90 then rotate around z 90 !=
Rotating around z 90 then rotate around x 90.

Memorizza la tua matrice di rotazione corrente e moltiplica ogni rotazione appena viene.


0

Oltre alla risposta @ concept3d è possibile utilizzare 3 matrici di rotazione estrinseche per ruotare attorno all'asse in coordinate mondiali. Citando da Wikipedia :

Le rotazioni estrinseche sono rotazioni elementali che si verificano attorno agli assi del sistema di coordinate fisse xyz. Il sistema XYZ ruota, mentre xyz è fisso. A partire da XYZ sovrapposto a xyz, è possibile utilizzare una composizione di tre rotazioni estrinseche per raggiungere qualsiasi orientamento target per XYZ. Gli angoli di Eulero o Tait Bryan (α, β, γ) sono le ampiezze di queste rotazioni elementali. Ad esempio, l'orientamento del bersaglio può essere raggiunto come segue:

Il sistema XYZ ruota attorno all'asse z di α. L'asse X è ora all'angolo α rispetto all'asse x.

Il sistema XYZ ruota di nuovo attorno all'asse x di β. L'asse Z è ora all'angolo β rispetto all'asse z.

Il sistema XYZ ruota di una terza volta attorno all'asse z di γ.

Le matrici di rotazione possono essere utilizzate per rappresentare una sequenza di rotazioni estrinseche. Per esempio,

R = Z (γ) Y (β) X (α)

rappresenta una composizione di rotazioni estrinseche attorno agli assi xyz, se utilizzata per pre-moltiplicare i vettori di colonna, mentre

R = X (α) Y (β) Z (γ)

rappresenta esattamente la stessa composizione se utilizzata per post-moltiplicare i vettori di riga.

Quindi ciò di cui hai bisogno è invertire l'ordine delle rotazioni in relazione a ciò che faresti usando le rotazioni intrinseche (o spazio locale). @Syntac ha chiesto una rotazione zxy, quindi dovremmo fare una rotazione estrinseca yxz per ottenere lo stesso risultato. Il codice è sotto:

Spiegazione dei valori della matrice qui .

// Init things.
D3DXMATRIX *rotationMatrixX = new D3DXMATRIX();
D3DXMATRIX *rotationMatrixY = new D3DXMATRIX();
D3DXMATRIX *rotationMatrixZ = new D3DXMATRIX();
D3DXMATRIX *resultRotationMatrix0 = new D3DXMATRIX();
D3DXMATRIX *resultRotationMatrix1 = new D3DXMATRIX();

D3DXMatrixRotationX(rotationMatrixX, angleX);
D3DXMatrixRotationY(rotationMatrixY, angleY);
D3DXMatrixRotationZ(rotationMatrixZ, angleZ);

// yx extrinsic rotation matrix
D3DXMatrixMultiply(resultRotationMatrix0, rotationMatrixY, rotationMatrixX);
// yxz extrinsic rotation matrix
D3DXMatrixMultiply(resultRotationMatrix1, resultRotationMatrix0, rotationMatrixZ);

D3DXVECTOR4* originalVector = // Original value to be transformed;
D3DXVECTOR4* transformedVector = new D3DXVECTOR4();

// Applying matrix to the vector.
D3DXVec4Transform(transformedVector, originalVector, resultRotationMatrix1);

// Don't forget to clean memory!

Questo codice è didattico, non ottimale, poiché è possibile riutilizzare diverse matrici D3DXMATRIX.


1
scusa amico, questo non è corretto. la moltiplicazione matrice / vettore è associativa. questo è esattamente lo stesso della moltiplicazione della matrice combinata.
concept3d

Hai ragione. Ho mescolato i concetti di rotazioni estrinseche e intrinseche.
dsilva.vinicius,

Riparerò questa risposta.
dsilva.vinicius,

La risposta è stata risolta ora.
dsilva.vinicius,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.