Risposte:
Sì, puoi aggiungere un vettore in caso di traduzione. Il motivo per utilizzare una matrice si riduce ad avere un modo uniforme per gestire diverse trasformazioni combinate.
Ad esempio, la rotazione viene generalmente eseguita utilizzando una matrice (controlla il commento di @MickLH per altri modi di gestire le rotazioni), quindi per gestire le trasformazioni multiple (rotazione / traslazione / ridimensionamento / proiezione ... ecc.) In modo uniforme, devi codificarli in una matrice.
Bene, più tecnicamente parlando; una trasformazione sta mappando un punto / vettore su un altro punto / vettore.
p` = T(p);
dove p` è il punto trasformato e T (p) è la funzione di trasformazione.
Dato che non utilizziamo una matrice, dobbiamo farlo per combinare più trasformazioni:
p1 = T (p);
p final = M (p1);
Non solo una matrice può combinare più tipi di trasformazioni in un'unica matrice (ad es. Affine, lineare, proiettiva).
L'uso di una matrice ci dà l'opportunità di combinare catene di trasformazioni e quindi moltiplicarle in batch. Questo ci fa risparmiare un sacco di cicli di solito dalla GPU (grazie a @ChristianRau per averlo sottolineato).
T final = T * R * P; // traduci ruota progetto
p final = T final * p;
È anche bene sottolineare che le GPU e persino alcune CPU sono ottimizzate per le operazioni vettoriali; Le CPU che utilizzano SIMD e GPU sono processori paralleli guidati dai dati in base alla progettazione, quindi l'utilizzo delle matrici si adatta perfettamente all'accelerazione hardware (in realtà, le GPU sono state progettate per adattarsi alle operazioni matrice / vettoriale).
Se tutto ciò che hai intenzione di fare è muoverti lungo un singolo asse e non applicare mai alcuna trasformazione, ciò che stai suggerendo va bene.
Il vero potere di usare una matrice è che puoi concatenare facilmente una serie di operazioni complesse insieme e applicare la stessa serie di operazioni a più oggetti.
La maggior parte dei casi non è così semplice e se si ruota prima l'oggetto, e si desidera trasformare lungo i suoi assi locali anziché gli assi del mondo, scoprirai che non puoi semplicemente aggiungere 10 a uno dei numeri e farlo funzionare correttamente .
Per rispondere in modo succinto alla domanda "perché", è perché una matrice 4x4 può descrivere contemporaneamente le operazioni di rotazione, traduzione e ridimensionamento. Essere in grado di descrivere uno di questi in modo coerente semplifica molte cose.
Diversi tipi di trasformazioni possono essere rappresentati più semplicemente con diverse operazioni matematiche. Come notate, la traduzione può essere fatta semplicemente aggiungendo. Ridimensionamento uniforme moltiplicando per uno scalare. Ma una matrice 4x4 opportunamente realizzata può fare qualsiasi cosa. Quindi l'utilizzo di 4x4 rende il codice e le interfacce molto più semplici. Paghi una certa complessità nella comprensione di questi 4x4, ma poi molte cose diventano più facili e veloci grazie a questo.
la ragione per usare una matrice 4x4 è che l'operazione è una trasformazione lineare . questo è un esempio di coordinate omogenee . La stessa cosa viene fatta nel caso 2d (usando una matrice 3x3). La ragione per usare coordinate omogenee è che tutte e 3 le trasformazioni geometriche possono essere fatte usando una sola operazione; altrimenti bisognerebbe fare una moltiplicazione di matrice 3x3 e un'aggiunta di matrice 3x3 (per la traduzione). questo link da cegprakash è utile.
Le traduzioni non possono essere rappresentate da matrici 3D
Un semplice argomento è che la traduzione può prendere il vettore di origine:
0
0
0
lontano dall'origine, dire a x = 1
:
1
0
0
Ma ciò richiederebbe una matrice tale che:
| a b c | |0| |1|
| d e f | * |0| = |0|
| g h i | |0| |0|
Ma questo è impossibile.
Un altro argomento è il teorema di decomposizione del valore singolare , secondo il quale ogni matrice può essere composta con due operazioni di rotazione e di ridimensionamento. Nessuna traduzione lì.
Perché le matrici possono essere utilizzate?
Molti oggetti modellati (ad es. Un telaio di un'auto) o parte di oggetti modellati (ad es. Una gomma di un'auto, una ruota motrice) sono solidi: le distanze tra i vertici non cambiano mai.
Le uniche trasformazioni che vogliamo fare su di esse sono rotazioni e traduzioni.
La moltiplicazione di matrici può codificare sia rotazioni che traduzioni.
Le matrici di rotazione hanno formule esplicite, ad esempio: una matrice di rotazione 2D per l'angolo a
è di forma:
cos(a) -sin(a)
sin(a) cos(a)
Esistono formule analoghe per il 3D , ma nota che le rotazioni 3D accettano 3 parametri anziché solo 1 .
Le traduzioni sono meno banali e verranno discusse in seguito. Sono la ragione per cui abbiamo bisogno di matrici 4D.
Perché è bello usare le matrici?
Perché la composizione di più matrici può essere pre-calcolata per moltiplicazione di matrici .
Ad esempio, se abbiamo intenzione di tradurre un migliaio di vettori v
del telaio della nostra auto con matrice T
e quindi ruotare con matrice R
, anziché fare:
v2 = T * v
e poi:
v3 = R * v2
per ogni vettore, possiamo pre-calcolare:
RT = R * T
e poi fai solo una moltiplicazione per ogni vertice:
v3 = RT * v
Ancora meglio: se vogliamo quindi posizionare i vertici di pneumatico e ruota motrice rispetto all'auto, moltiplichiamo semplicemente la matrice precedente RT
per la matrice relativa all'auto stessa.
Ciò porta naturalmente a mantenere una pila di matrici:
Come l'aggiunta di una dimensione risolve il problema
Consideriamo il caso da 1D a 2D che è più facile da visualizzare.
Una matrice in 1D è solo un numero e, come abbiamo visto in 3D, non può fare una traduzione, solo un ridimensionamento.
Ma se aggiungiamo la dimensione extra come:
| 1 dx | * |x| = | x + dx |
| 0 1 | |1| | 1 |
e poi dimentichiamo la nuova dimensione extra, otteniamo:
x + dx
come volevamo.
Questa trasformazione 2D è così importante che ha un nome: trasformazione di taglio .
È bello visualizzare questa trasformazione:
Nota come ogni linea orizzontale (fissa y
) è appena tradotta.
Ci è capitato di prendere la linea y = 1
come nostra nuova linea 1D e tradurla con una matrice 2D.
Le cose sono analoghe in 3D, con matrici di taglio 4D della forma:
| 1 0 0 dx | | x | | x + dx |
| 0 1 0 dy | * | y | = | y + dy |
| 0 0 1 dz | | z | | z + dz |
| 0 0 0 1 | | 1 | | 1 |
E le nostre vecchie rotazioni / ridimensionamenti 3D sono ora di forma:
| a b c 0 |
| d e f 0 |
| g h i 0 |
| 0 0 0 1 |
Vale la pena guardare anche questo video tutorial di Jamie King .
Spazio affine
Lo spazio affine è lo spazio generato da tutte le nostre trasformazioni lineari 3D (moltiplicazioni di matrice) insieme al taglio 4D (traduzioni 3D).
Se moltiplichiamo una matrice di taglio e una trasformazione lineare 3D, otteniamo sempre qualcosa della forma:
| a b c dx |
| d e f dy |
| g h i dz |
| 0 0 0 1 |
Questa è la trasformazione affine più generale possibile, che fa rotazione / ridimensionamento 3D e traduzione.
Una proprietà importante è che se moltiplichiamo 2 matrici affine:
| a b c dx | | a2 b2 c2 dx2 |
| d e f dy | * | d2 e2 f2 dy2 |
| g h i dz | | g2 h2 i2 dz2 |
| 0 0 0 1 | | 0 0 0 1 |
otteniamo sempre un'altra matrice affine di forma:
| a3 b3 c3 (dx + dx2) |
| d3 e3 f3 (dy + dy2) |
| g3 h3 i3 (dz + dz2) |
| 0 0 0 1 |
I matematici chiamano questa chiusura di proprietà ed è necessario per definire uno spazio.
Per noi significa che possiamo continuare a fare moltiplicazioni di matrice per calcolare felicemente le trasformazioni finali, motivo per cui in primo luogo utilizzare le matrici usate, senza mai ottenere trasformazioni lineari 4D più generali che non sono affini.
Proiezione di frustum
Ma aspetta, c'è un'altra trasformazione importante che facciamo continuamente: glFrustum
che rende un oggetto 2 volte più grande, appare 2 volte più piccolo.
Per prima cosa prendi un po 'di intuizione sul glOrtho
vs glFrustum
su: https://stackoverflow.com/questions/2571402/explain-the-usage-of-glortho/36046924#36046924
glOrtho
può essere fatto solo con traduzioni + ridimensionamento, ma come possiamo implementare glFrustum
con le matrici?
Supporre che:
z = -1
un quadrato di lunghezza 2z = -2
Se solo permettessimo 4 vettori più generali di tipo:
(x, y, z, w)
con w != 0
, e inoltre identifichiamo ogni (x, y, z, w)
con (x/w, y/w, z/w, 1)
, quindi una trasformazione del frustum con la matrice sarebbe:
| 1 0 0 0 | | x | | x | | x / -z |
| 0 1 0 0 | * | y | = | y | identified to | y / -z |
| 0 0 1 0 | | z | | z | | -1 |
| 0 0 -1 0 | | w | | -z | | 0 |
Se buttiamo via z
e w
alla fine, otteniamo:
x_proj = x / -z
y_proj = y / -z
che è esattamente quello che volevamo! Possiamo verificarlo per alcuni valori, ad esempio:
z == -1
, esattamente sull'aereo su cui stiamo proiettando, x_proj == x
ey_proj == y
.z == -2
, then x_proj = x/2
: gli oggetti hanno la metà.Nota come la glFrustum
trasformazione non sia di forma affine: non può essere implementata solo con rotazioni e traduzioni.
Il "trucco" matematico di aggiungere w
e dividere per esso si chiama coordinate omogenee
Vedi anche: domanda Stack Overflow correlata: https://stackoverflow.com/questions/2465116/understanding-opengl-matrices
Guarda questo video per comprendere i concetti di modello, vista e proiezione.
Le matrici 4x4 non vengono utilizzate solo per la traduzione di un oggetto 3D. Ma anche per vari altri scopi.
Vedi questo per capire come i vertici nel mondo sono rappresentati come matrici 4D e come vengono trasformati.