¿Cómo rotar un objeto alrededor de ejes alineados por el mundo?

15

Tengo un Vector3 que tiene un ángulo de Euler para cada eje.

Por lo general, cuando quiero crear una matriz de rotación, usaré funciones como D3DXMatrixRotationX que pasa el ángulo respectivo desde mi vector de rotación anterior y multiplico las matrices (ZXY) para crear la matriz de rotación general que se usa para formar la matriz de transformación de objetos completa.

Sin embargo, este método producirá un conjunto de rotaciones en el espacio del objeto. Es decir, pasar un vector de (90, 0, 90) a mi método creará efectivamente una rotación en el espacio mundial de (90, 90, 0).

¿Hay alguna manera de garantizar siempre que cada componente de mi vector de rotación dé como resultado una rotación alrededor de los respectivos ejes alineados con el espacio mundial?

EDITAR:

Esta es una animación de lo que está sucediendo actualmente: quiero una forma de rotar alrededor de los ejes azules, no los rojos.

Euler Angles

EDITAR 2:

Solo para tener en cuenta que no estoy buscando una solución que implique los ángulos de Euler, sino simplemente una forma en que pueda representar una transformación de múltiples rotaciones alrededor de los ejes del mundo.

Syntac_
fuente
¿Qué tiene de malo simplemente llamar tres veces a las funciones denetnet y filtrar las partes de los vectores que no desea (configurándolas a 0 antes de llamar a la función)? De lo contrario, no estoy seguro de lo que estás tratando de lograr.
TravisG
¿Filtrando qué? Llamo a las 3 funciones separadas y luego las multiplico para crear la matriz de transformación. Sin embargo, esto archiva una rotación local.
Syntac_
¿Quieres ángulos de Euler o rotación sobre ejes mundiales? Tenga en cuenta que, según la definición de los ángulos de Euler (por ejemplo, en.wikipedia.org/wiki/Euler_angles ), solo el ángulo alfa se refiere estrictamente a un eje mundial. Los otros dos ángulos son relativos a ejes inclinados que no necesariamente coinciden con los ejes mundiales.
DMGregory
1
Usando los ángulos de Euler, estás multiplicando las tres matrices de rotación antes de aplicarlas en el vértice. Si M, N, O son las matrices de rotación, la operación de resultado es MNO v. Lo que he propuesto es aplicar cada matriz por separado: v1 = O v0, luego v2 = N v1 y finalmente v3 = M v2. De esta manera, cada vi estará en coordenadas mundiales y solo necesitará usar una matriz de rotación para el eje actual en las coordenadas mundiales también.
dsilva.vinicius
3
@ dsilva.vinicius Sus transformaciones separadas son exactamente las mismas que las combinadas, o para decirlo de otra manera: MNO v == M * (N * (O v))
GuyRT

Respuestas:

1

Según sus comentarios, parece que está almacenando la orientación del objeto como un conjunto de ángulos de Euler y disminuyendo / disminuyendo los ángulos cuando el jugador gira el objeto. Es decir, tienes algo como este pseudocódigo:

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

Como señala Charles Beattie , debido a que las rotaciones no conmutan, esto no funcionará como se espera a menos que el jugador rote el objeto en el mismo orden en que eulerAnglesToMatrix()aplica las rotaciones.

En particular, considere la siguiente secuencia de rotaciones:

  1. girar el objeto en x grados alrededor del eje X;
  2. girar el objeto en y grados alrededor del eje Y;
  3. girar el objeto en - x grados alrededor del eje X;
  4. gire el objeto en - y grados alrededor del eje Y.

En la ingenua representación de ángulo de Euler, como se implementa en el pseudocódigo anterior, estas rotaciones se cancelarán y el objeto volverá a su orientación original. En el mundo real, esto no sucede: si no me crees, toma un dado de seis lados o un cubo de Rubik, deja que x = y = 90 °, ¡y pruébalo tú mismo!

La solución, como notas en tu propia respuesta , es almacenar la orientación del objeto como una matriz de rotación (o un cuaternión) y actualizar esa matriz en función de las aportaciones del usuario. Es decir, en lugar del pseudocódigo anterior, haría algo como esto:

// 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!

(Técnicamente, dado que cualquier matriz de rotación o cuaternión se puede representar como un conjunto de ángulos de Euler, es posible usarlos para almacenar la orientación del objeto. Pero la regla físicamente correcta para combinar dos rotaciones secuenciales, cada una representada como ángulos de Euler, en una sola rotación es bastante complicado, y esencialmente equivale a convertir las rotaciones en matrices / cuaterniones, multiplicarlas y luego convertir el resultado nuevamente en ángulos de Euler).

Ilmari Karonen
fuente
Sí, tienes razón, esta fue la solución. Siento que esto es un poco mejor que la respuesta de concept3d, ya que da la impresión de que se necesita un cuaternión, pero eso no es cierto. Mientras almacenara la rotación actual como una matriz y no los tres ángulos de Euler, estaba bien.
Syntac_
15

El problema con las rotaciones es que la mayoría de las personas piensan en términos de ángulos de Euler, ya que son fáciles de entender.

Sin embargo, la mayoría de la gente olvida el punto de que Euler se inclina son tres ángulos secuenciales . Esto significa que la rotación alrededor del primer eje hará que la siguiente rotación sea relativa a la primera rotación original, por lo tanto, no puede rotar independientemente un vector alrededor de cada uno de los 3 ejes usando ángulos de Euler.

Esto se traduce directamente en matrices cuando multiplica dos matrices, puede pensar que esta multiplicación transforma una matriz en el espacio de la otra matriz.

Esto debe suceder con 3 rotaciones secuenciales, incluso cuando se usan cuaterniones.

ingrese la descripción de la imagen aquí

Quiero enfatizar el hecho de que los cuaterniones no son una solución para el bloqueo de gimble. En realidad, el bloqueo de gimble siempre sucederá si representó ángulos de Euler usando cuaterniones. El problema no es la representación, el problema es la 3 pasos secuenciales.

¿La solución?

La solución para rotar un vector alrededor de 3 ejes independientemente es combinarlo en un solo eje y un solo ángulo, de esta manera puede deshacerse del paso en el que tiene que hacer una multiplicación secuencial. Esto se traducirá efectivamente en:

Mi matriz de rotación representa el resultado de la rotación alrededor de X e Y y Z.

en lugar de la interpretación de Euler de

Mi matriz de rotación representa la rotación alrededor de X, luego Y y luego Z.

Para aclarar esto, citaré del teorema de rotación de Euler de Wikipedia:

Según el teorema de rotación de Euler, cualquier rotación o secuencia de rotaciones de un cuerpo rígido o sistema de coordenadas sobre un punto fijo es equivalente a una rotación simple por un ángulo dado given sobre un eje fijo (llamado eje de Euler) que atraviesa el punto fijo. El eje de Euler normalmente está representado por un vector unitario u →. Por lo tanto, cualquier rotación en tres dimensiones puede representarse como una combinación de un vector u → y un escalar θ. Los cuaterniones brindan una manera simple de codificar esta representación de eje-ángulo en cuatro números, y aplicar la rotación correspondiente a un vector de posición que representa un punto relativo al origen en R3.

Tenga en cuenta que multiplicar 3 matrices siempre representará 3 rotaciones secuenciales.

Ahora para combinar rotaciones alrededor de 3 ejes, debe obtener un solo eje y ángulos únicos que representen la rotación alrededor de X, Y, Z. En otras palabras, debe usar una representación de eje / ángulo o cuaternión para deshacerse de las rotaciones secuenciales.

Esto generalmente se hace, comenzando con una orientación inicial (la orientación puede considerarse como un ángulo de eje), generalmente representada como un cuaternión o un ángulo de eje, y luego modificando esa orientación para representar su orientación de destino. Por ejemplo, comienza con el quaterion de identidad y luego gira por la diferencia para llegar a la orientación de destino. De esta manera no pierdes ningún grado de libertad.

concepto3d
fuente
Marcado como respuesta, ya que parece perspicaz.
Syntac_
Tengo algunos problemas para entender lo que intentas decir con esta respuesta. ¿Es simplemente "no almacene la orientación de un objeto como ángulos de Euler"? Y si es así, ¿por qué no decirlo?
Ilmari Karonen
@IlmariKaronen Podría decirse más claramente, pero creo que concept3d está promoviendo la representación del ángulo del eje; Consulte la sección 1.2.2 de este documento para ver la relación entre el ángulo del eje y los cuaterniones. La representación de ángulo de eje es más fácil de implementar por las razones anteriores, no sufre de bloqueo de cardán y (al menos para mí) es tan fácil de entender como los ángulos de Euler.
NauticalMile
@ concept3d, eso es muy interesante, y realmente me gusta tu respuesta. Sin embargo, me falta algo: la gente interactúa con la computadora usando un teclado y un mouse, si pensamos en el mouse, entonces estamos hablando de deltas del mouse x e y. ¿Cómo representar estos deltas x, y con un solo cuaternión que podemos usar para generar la matriz de rotación, por ejemplo, para cambiar la orientación de un objeto?
gmagno
@gmagno, el enfoque suele ser proyectar el movimiento del mouse sobre los objetos o la escena y calcular los deltas en ese espacio; para ello, proyecta un rayo y calcula la intersección. Búsqueda de proyección de rayos, proyecto y desproyección, soy duro con los detalles ya que no trabajé en CG durante años. Espero que ayude.
concept3d
2

Cambiar una combinación de rotaciones del espacio de objetos al espacio mundial es trivial: solo tiene que invertir el orden en que se aplican las rotaciones.

En su caso, en lugar de multiplicar matrices Z × X × Y, solo necesita calcular Y × X × Z.

Se puede encontrar una razón para esto en Wikipedia: conversión entre rotaciones intrínsecas y extrínsecas .

sam hocevar
fuente
Si eso fuera cierto, entonces la siguiente declaración de su fuente no sería verdadera, porque las rotaciones serían diferentes: "Cualquier rotación extrínseca es equivalente a una rotación intrínseca en los mismos ángulos pero con un orden invertido de rotaciones elementales, y viceversa ".
Syntac_
1
No veo contradicción aquí. Tanto mi respuesta como esa afirmación son ciertas. Y sí, realizar rotaciones en el espacio de objetos y en el espacio mundial produce diferentes rotaciones; ese es precisamente el punto, ¿no?
sam hocevar
Esa declaración dice que cambiar el orden siempre dará como resultado la misma rotación. Si una orden produce una rotación incorrecta, la otra orden también lo hará, lo que significa que no es una solución.
Syntac_
1
Estás leyendo mal. Cambiar el orden no da como resultado la misma rotación. Cambiar el orden y cambiar de rotaciones intrínsecas a rotaciones extrínsecas da como resultado la misma rotación.
sam hocevar
1
No creo entender tu pregunta. Su GIF muestra una rotación de aproximadamente 50 grados alrededor Z(espacio del objeto), luego 50 grados alrededor X(espacio del objeto), luego 45 grados alrededor Y(espacio del objeto). Esto es exactamente lo mismo que una rotación de 45 grados alrededor Y( espacio mundial ), luego 50 grados alrededor X( espacio mundial ), luego 50 grados alrededor Z( espacio mundial ).
sam hocevar
1

Proporcionaré mi solución como respuesta hasta que alguien pueda explicar por qué funciona.

Cada render estaba reconstruyendo mi quaternion usando los ángulos almacenados en mi vector de rotación y luego aplicando el quaternion a mi transformación final.

Sin embargo, para mantenerlo alrededor de los ejes mundiales, tuve que retener el cuaternión en todos los cuadros y solo rotar objetos usando una diferencia de ángulo, es decir ...

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

(Detallado para readiblity)

Creo que dsilva.vinicius estaba tratando de llegar a este punto.

Syntac_
fuente
1

Deberá almacenar el orden de las rotaciones.

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

Almacene su matriz de rotación actual y premultiply cada rotación como vienen.

Charles Beattie
fuente
0

Además de la respuesta @ concept3d, puede usar 3 matrices de rotación extrínsecas para rotar alrededor del eje en coordenadas mundiales. Citando de Wikipedia :

Las rotaciones extrínsecas son rotaciones elementales que ocurren alrededor de los ejes del sistema de coordenadas fijo xyz. El sistema XYZ gira, mientras que xyz es fijo. Comenzando con XYZ solapando xyz, se puede usar una composición de tres rotaciones extrínsecas para alcanzar cualquier orientación objetivo para XYZ. Los ángulos de Euler o Tait Bryan (α, β, γ) son las amplitudes de estas rotaciones elementales. Por ejemplo, la orientación del objetivo se puede alcanzar de la siguiente manera:

El sistema XYZ gira alrededor del eje z en α. El eje X ahora está en ángulo α con respecto al eje x.

El sistema XYZ gira nuevamente alrededor del eje x por β. El eje Z ahora está en ángulo β con respecto al eje z.

El sistema XYZ gira por tercera vez alrededor del eje z en γ.

Las matrices de rotación se pueden usar para representar una secuencia de rotaciones extrínsecas. Por ejemplo,

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

representa una composición de rotaciones extrínsecas sobre los ejes xyz, si se usa para multiplicar previamente los vectores de columna, mientras que

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

representa exactamente la misma composición cuando se usa para post-multiplicar vectores de fila.

Entonces, lo que necesita es invertir el orden de las rotaciones en relación con lo que haría utilizando rotaciones intrínsecas (o espacio local). @Syntac solicitó una rotación zxy, por lo que deberíamos hacer una rotación extrínseca yxz para lograr el mismo resultado. El código está abajo:

Explicación de los valores de la matriz aquí .

// 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!

Este código es didáctico, no óptimo, ya que podría reutilizar varias matrices D3DXMATRIX.

dsilva.vinicius
fuente
1
lo siento hombre esto no es correcto La multiplicación matriz / vector es asociativa. esto es exactamente lo mismo que la multiplicación combinada de matrices.
concept3d
Tienes razón. He mezclado los conceptos de rotaciones extrínsecas e intrínsecas.
dsilva.vinicius
Arreglaré esta respuesta.
dsilva.vinicius
La respuesta está arreglada ahora.
dsilva.vinicius