¿Cómo se puede extraer la orientación de una matriz de transformación?

10

Tengo una matriz de transformación 4x4 M, y quiero encontrar la forma de una esfera cuando es transformada por M. (Esfera está en el origen y tiene radio 1.)

Sé que puedo encontrar el centro simplemente multiplicando M por (0,0,0,1).

Sin embargo, el radio se convierte en un problema ya que M puede aplastar y rotar la esfera. ¿Cómo puedo averiguar los nuevos radios del elipsoide resultante? ¿Hay alguna forma de averiguar la orientación?

Más específicamente, necesito saber el tamaño de la esfera delimitadora que encerraría la esfera transformada. En otras palabras, ¿cuál es el máximo de | M * V - M * (0,0,0,1) |, donde V es un vector unitario (un punto en la esfera original).

CaptainCodeman
fuente
1
¿No puedes calcular la longitud de los vectores de ejes transformados? (3 columnas de la parte de rotación de su matriz) La esfera delimitadora tendría un radio igual a la longitud del vector más largo.
Bart
No, no creo que sea correcto. La dirección más larga puede no estar alineada con el eje. (Imagínese si lo aplasta, lo gira, lo vuelve a aplastar, lo gira un poco más, etc.)
CaptainCodeman
Hmm, no estoy seguro de que eso importe. Si logro convencerme, escribiré una respuesta más tarde hoy. ;)
Bart
El problema es que, si realiza la transformación ESCALA, los vectores base de la matriz M no tienen que permanecer ORTOGONALES entre sí.
GPUquant

Respuestas:

6

Matemáticamente, la cantidad que está preguntando se llama la norma del operador . Desafortunadamente, no hay una fórmula simple para ello. Si es una transformación afín totalmente general, por ejemplo, si pudiera tener una combinación arbitraria de rotaciones y escalas no uniformes, en cualquier orden, entonces me temo que no hay nada más que usar la descomposición de valores singulares . Si aplica SVD a su matriz, el valor singular más grande será el radio máximo del elipsoide resultante. Los otros valores singulares también serán sus otros dos radios, y el procedimiento SVD también puede extraer la orientación de los ejes por usted.

La implementación de SVD no es para los débiles de corazón, ya que implica encontrar valores propios. Si todo lo que quiere son los valores singulares en sí, son las raíces cuadradas de los valores propios de M ^ T * M. Entonces, si tiene a mano un solucionador de valores propios 3x3, o no le importa escribir uno, puede usarlo. Si desea extraer también las orientaciones de los ejes, entonces se involucra más, ya que también tiene que encontrar vectores propios. En ese artículo de Wikipedia hay una lista de enlaces a bibliotecas para hacer SVD, uno de los cuales puede usar en su proyecto.

Si la forma de su matriz está restringida de tal manera que la escala no uniforme ocurre como máximo una vez y es la primera transformación aplicada, es decir, es la más correcta cuando está utilizando vectores de columna, entonces puede simplificar esto para ver solo las longitudes de vectores de eje transformados. Solo en ese caso, es decir, una sola escala no uniforme seguida de cualquier secuencia de rotaciones, reflexiones y escalas uniformes, mirar solo los vectores del eje le dará la respuesta correcta.

Nathan Reed
fuente
Gracias, agradezco la respuesta detallada. ¿Dónde no funciona la descomposición proporcionada en la otra respuesta?
CaptainCodeman
2
@CaptainCodeman La otra respuesta es solo mirar los vectores de eje transformados (es decir, las columnas de la matriz), como lo que describí en mi tercer párrafo. Falla en el caso de que haya una escala no uniforme después de una rotación, ya que la escala no se aplica a lo largo de los ejes originales.
Nathan Reed
2

Quizás extraiga factores de escala de la matriz y luego use el valor máximo de sus componentes. Usando la matriz SRT (Scale-Rotation-Translation) puede hacer esto así:

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

(basado en http://wklej.org/id/950061/ - el nombre es descomposeTRS y no decomposeSRT porque uso nombres superpuestos en orden cuyas matrices se multiplican en OpenGL).

Ahora puede multiplicar el radio de la esfera original por scaleFactor y tendrá su esfera delimitadora.

solapa
fuente