Come è possibile estrarre l'orientamento da una matrice di trasformazione?


10

Ho una matrice di trasformazione 4x4 M e voglio scoprire la forma di una sfera quando viene trasformata da M. (La sfera è all'origine e ha raggio 1)

So di poter trovare il centro semplicemente moltiplicando M per (0,0,0,1).

Tuttavia, il raggio diventa un problema poiché M può schiacciare e ruotare la sfera. Come posso scoprire i nuovi raggi dell'ellissoide risultante? C'è un modo per scoprire l'orientamento?

Più specificamente, ho bisogno di conoscere le dimensioni della sfera che circonda la sfera trasformata. In altre parole, qual è il massimo di | M * V - M * (0,0,0,1) |, dove V è un vettore unitario (un punto sulla sfera originale).


1
Non puoi semplicemente calcolare la lunghezza dei vettori degli assi trasformati? (3 colonne della parte di rotazione della tua matrice) La sfera di delimitazione avrebbe un raggio uguale alla lunghezza del vettore più lungo.
Bart,

No, non penso sia corretto. La direzione più lunga potrebbe non essere allineata agli assi. (Immagina di schiacciarlo, ruotarlo, di nuovo schiacciarlo, ruotarlo ancora un po ', ecc.)
CaptainCodeman

Hmm, non sono sicuro che sia importante. Se riesco a convincermi, scriverò una risposta più tardi oggi. ;)
Bart

Il problema è che, se si esegue la trasformazione SCALE, i vettori di base della matrice M non devono rimanere ORTOGONALI l'uno rispetto all'altro.
GPUquant

Risposte:


6

Matematicamente, la quantità che stai chiedendo è chiamata la norma dell'operatore . Sfortunatamente, non esiste una formula semplice per questo. Se si tratta di una trasformazione affine del tutto generale - ad esempio, se potesse avere una combinazione arbitraria di rotazioni e scale non uniformi, in qualsiasi ordine - allora temo non ci sia altro che usare una scomposizione a valore singolare . Se applichi SVD alla tua matrice, il valore singolare più grande sarà il raggio massimo dell'ellissoide risultante. Gli altri valori singolari saranno anche i suoi altri due raggi e la procedura SVD può anche estrarre l'orientamento degli assi per te.

L'implementazione di SVD non è per i deboli di cuore, in quanto comporta la ricerca di autovalori. Se tutto ciò che vuoi sono i valori singolari stessi, sono le radici quadrate degli autovalori di M ^ T * M. Quindi se hai un risolutore di autovalori 3x3 a portata di mano, o non ti dispiace scriverne uno, puoi usarlo. Se vuoi estrarre anche gli orientamenti degli assi, allora diventa più coinvolto in quanto devi trovare anche gli autovettori. In quell'articolo di Wikipedia c'è un elenco di collegamenti a librerie per fare SVD, uno dei quali potresti essere in grado di usare nel tuo progetto.

Se la forma della tua matrice è limitata in modo tale che la scala non uniforme si verifichi al massimo una volta ed è la prima trasformazione applicata, ovvero è la più giusta quando stai usando i vettori di colonna, allora puoi semplificarla per vedere solo le lunghezze della vettori di assi trasformati. Solo in quel caso - ovvero una singola scala non uniforme seguita da una sequenza di rotazioni, riflessioni e scale uniformi - guardare solo i vettori degli assi ti darà la risposta giusta.


Grazie, apprezzo la risposta dettagliata. Dove non funziona la decomposizione fornita nell'altra risposta?
CaptainCodeman,

2
@CaptainCodeman L'altra risposta è solo guardare i vettori degli assi trasformati (cioè le colonne della matrice), come quello che ho descritto nel mio terzo paragrafo. Non riesce nel caso in cui vi sia una scala non uniforme dopo una rotazione, da allora il ridimensionamento non si applica lungo gli assi originali.
Nathan Reed,

2

Forse estrarre i fattori di scala dalla matrice e quindi utilizzare il valore massimo dei suoi componenti. Usando la matrice SRT (Scale-Rotation-Translation) puoi farlo in questo modo:

glm::mat4 m = ...;
// Extract col vectors of the matrix
glm::vec3 col1(m[0][0], m[0][1], m[0][2]);
glm::vec3 col2(m[1][0], m[1][1], m[1][2]);
glm::vec3 col3(m[2][0], m[2][1], m[2][2]);
//Extract the scaling factors
glm::vec3 scaling;
scaling.x = glm::length(col1);
scaling.y = glm::length(col2);
scaling.z = glm::length(col3);

float scaleFactor = MAX(scaling.x, MAX(scaling.y, scaling.z));

(basato su http://wklej.org/id/950061/ - il nome è decomposeTRS e non decompose SRT perché utilizzo nomi inclusi nell'ordine in cui le matrici vengono moltiplicate in OpenGL).

Ora puoi moltiplicare il raggio della sfera originale per scaleFactor e hai la tua sfera di delimitazione.

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.