Jak wyodrębnić orientację z macierzy transformacji?

10

Mam macierz transformacji 4x4 M i chcę znaleźć kształt kuli po transformacji przez M. (Kula jest na początku i ma promień 1).

Wiem, że mogę znaleźć środek, po prostu mnożąc M przez (0,0,0,1).

Jednak promień staje się problemem, ponieważ M może zgnieść i obrócić kulę. Jak mogę znaleźć nowy promień (-y) powstałej elipsoidy? Czy jest jakiś sposób, aby ustalić orientację?

Mówiąc dokładniej, muszę znać rozmiar kuli ograniczającej, która otaczałaby kulę transformowaną. Innymi słowy, jaka jest maksymalna wartość | M * V - M * (0,0,0,1) |, gdzie V jest wektorem jednostkowym (punkt na oryginalnej kuli).

CaptainCodeman
źródło
1
Czy nie możesz po prostu obliczyć długości wektorów transformowanych osi? (3 kolumny rotacyjnej części macierzy) Kula graniczna miałaby promień równy długości najdłuższego wektora.
Bart
Nie, nie sądzę, że to prawda. Najdłuższy kierunek może nie być wyrównany względem osi. (Wyobraź sobie, że zgniotłeś go, obróciłeś, zgniotłeś go jeszcze raz, obróciłeś jeszcze trochę itp.)
obróciłeś, zgniotłeś obróciłeś
Hmm, nie jestem pewien, czy to się liczy. Jeśli uda mi się przekonać, napiszę odpowiedź dzisiaj. ;)
Bart
Problem polega na tym, że jeśli wykonasz transformację SCALE, wektory podstawowe macierzy M nie muszą pozostać względem siebie ORTOGONALNE.
GPUquant

Odpowiedzi:

6

Matematycznie ilość, o którą pytasz, nazywa się normą operatora . Niestety nie ma na to prostej formuły. Jeśli jest to w pełni ogólna transformacja afiniczna - na przykład, jeśli mogłaby mieć dowolną kombinację rotacji i niejednorodnych skal, w dowolnej kolejności - obawiam się, że nie ma nic innego, jak tylko zastosować rozkład pojedynczej wartości . Jeśli zastosujesz SVD do swojej matrycy, największą wartością pojedynczą będzie maksymalny promień powstałej elipsoidy. Innymi wartościami osobliwymi będą również pozostałe dwa promienie, a procedura SVD może również wyodrębnić dla Ciebie orientację osi.

Wdrożenie SVD nie jest przeznaczone dla osób o słabym sercu, ponieważ polega na znalezieniu wartości własnych. Jeśli wszystko, czego chcesz, to same wartości osobliwe, są one pierwiastkami kwadratowymi wartości własnych M ^ T * M. Więc jeśli masz pod ręką solver wartości własnej 3x3 lub nie masz nic przeciwko napisaniu jednej, możesz tego użyć. Jeśli chcesz również wyodrębnić orientacje osi, to angażuje się bardziej, ponieważ musisz również znaleźć wektory własne. W tym artykule w Wikipedii znajduje się lista linków do bibliotek do wykonywania SVD, z których jeden możesz wykorzystać w swoim projekcie.

Jeśli forma macierzy jest ograniczona w taki sposób, że nierównomierna skala zachodzi co najwyżej raz i jest stosowana pierwsza transformacja, tj. Jest najdalsza po prawej, gdy używasz wektorów kolumnowych, możesz to uprościć, aby spojrzeć na długości transformowane wektory osi. Tylko w takim przypadku - tj. Pojedyncza niejednolita skala, po której następuje dowolna sekwencja obrotów, odbić i jednolite skale - patrząc tylko na wektory osi da właściwą odpowiedź.

Nathan Reed
źródło
Dziękuję, doceniam szczegółową odpowiedź. Gdzie rozkład podany w drugiej odpowiedzi nie działa?
CaptainCodeman
2
@CaptainCodeman Inną odpowiedzią jest po prostu spojrzenie na transformowane wektory osi (tj. Kolumny macierzy), podobnie jak opisałem w trzecim akapicie. Nie udaje się to w przypadku, gdy po obrocie występuje niejednorodna skala, ponieważ wówczas skalowanie nie ma zastosowania wzdłuż pierwotnych osi.
Nathan Reed
2

Może wyodrębnij współczynniki skali z macierzy, a następnie użyj maksymalnej wartości jej składników. Za pomocą macierzy SRT (Scale-Rotation-Translation) możesz to zrobić w następujący sposób:

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

(na podstawie http://wklej.org/id/950061/ - nazwa to decomposeTRS, a nie decomposeSRT, ponieważ używam nazw zapisywanych w kolejności, które macierze są mnożone w OpenGL).

Teraz możesz pomnożyć oryginalny promień kuli przez scaleFactor i masz swoją kulę ograniczającą.

revers
źródło