Estoy tratando de permitir que el usuario de mi aplicación gire un objeto 3D dibujado en el centro de la pantalla arrastrando su dedo sobre la pantalla. Un movimiento horizontal en la pantalla significa rotación alrededor de un eje Y fijo, y un movimiento vertical significa rotación alrededor del eje X. El problema que tengo es que si solo permito la rotación alrededor de un eje, el objeto gira bien, pero tan pronto como introduzco una segunda rotación, el objeto no gira como se esperaba.
Aquí hay una imagen de lo que está sucediendo:
El eje azul representa mi eje fijo. Imagen de la pantalla que tiene este eje azul fijo. Esto es a lo que quiero que rote el objeto en relación. Lo que está sucediendo está en rojo.
Esto es lo que sé:
- La primera rotación alrededor de Y (0, 1, 0) hace que el modelo se mueva del espacio azul (llame a este espacio A) a otro espacio (llame a este espacio B)
- Intentar rotar nuevamente usando el vector (1, 0, 0) gira alrededor del eje x en el espacio B NO en el espacio A, que no es lo que quiero hacer.
Esto es lo que probé, dado lo que (creo) que sé (dejando de lado la W coord por brevedad):
- Primero gire alrededor de Y (0, 1, 0) usando un Quaternion.
- Convierta la rotación Y Quaternion en una matriz.
- Multiplique la matriz de rotación Y por mi eje fijo x Vector (1, 0, 0) para obtener el eje X en relación con el nuevo espacio.
- Gire alrededor de este nuevo Vector X usando un Quaternion.
Aquí está el código:
private float[] rotationMatrix() {
final float[] xAxis = {1f, 0f, 0f, 1f};
final float[] yAxis = {0f, 1f, 0f, 1f};
float[] rotationY = Quaternion.fromAxisAngle(yAxis, -angleX).toMatrix();
// multiply x axis by rotationY to put it in object space
float[] xAxisObjectSpace = new float[4];
multiplyMV(xAxisObjectSpace, 0, rotationY, 0, xAxis, 0);
float[] rotationX = Quaternion.fromAxisAngle(xAxisObjectSpace, -angleY).toMatrix();
float[] rotationMatrix = new float[16];
multiplyMM(rotationMatrix, 0, rotationY, 0, rotationX, 0);
return rotationMatrix;
}
Esto no funciona como espero. La rotación parece funcionar, pero en algún momento el movimiento horizontal no gira sobre el eje Y, parece girar sobre el eje Z.
No estoy seguro si mi comprensión es incorrecta o si algo más está causando un problema. Tengo algunas otras transformaciones que estoy haciendo al objeto además de la rotación. Muevo el objeto al centro antes de aplicar la rotación. Lo giro usando la matriz devuelta por mi función anterior, luego lo traduzco -2 en la dirección Z para poder ver el objeto. No creo que esto esté arruinando mis rotaciones, pero de todos modos aquí está el código para eso:
private float[] getMvpMatrix() {
// translates the object to where we can see it
final float[] translationMatrix = new float[16];
setIdentityM(translationMatrix, 0);
translateM(translationMatrix, 0, translationMatrix, 0, 0f, 0f, -2);
float[] rotationMatrix = rotationMatrix();
// centers the object
final float[] centeringMatrix = new float[16];
setIdentityM(centeringMatrix, 0);
float moveX = (extents.max[0] + extents.min[0]) / 2f;
float moveY = (extents.max[1] + extents.min[1]) / 2f;
float moveZ = (extents.max[2] + extents.min[2]) / 2f;
translateM(centeringMatrix, 0, //
-moveX, //
-moveY, //
-moveZ //
);
// apply the translations/rotations
final float[] modelMatrix = new float[16];
multiplyMM(modelMatrix, 0, translationMatrix, 0, rotationMatrix, 0);
multiplyMM(modelMatrix, 0, modelMatrix, 0, centeringMatrix, 0);
final float[] mvpMatrix = new float[16];
multiplyMM(mvpMatrix, 0, projectionMatrix, 0, modelMatrix, 0);
return mvpMatrix;
}
He estado atrapado en esto por unos días. La ayuda es muy apreciada.
================================================== ================
ACTUALIZAR:
Hacer que esto funcione en Unity es sencillo. Aquí hay un código que gira un cubo centrado en el origen:
public class CubeController : MonoBehaviour {
Vector3 xAxis = new Vector3 (1f, 0f, 0f);
Vector3 yAxis = new Vector3 (0f, 1f, 0f);
// Update is called once per frame
void FixedUpdate () {
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");
transform.Rotate (xAxis, vertical, Space.World);
transform.Rotate (yAxis, -horizontal, Space.World);
}
}
La parte que hace que las rotaciones se comporten como espero es el Space.World
parámetro de la Rotate
función en la transformación.
Si pudiera usar Unity lo haría, desafortunadamente tengo que codificar este comportamiento yo mismo.
Respuestas:
El problema que tienes se llama gimble lock . Creo que lo que estás buscando hacer se llama rotación de bola de arco . Las matemáticas para el arcball pueden ser un poco complicadas.
Una forma más sencilla de hacerlo es encontrar un vector 2D perpendicular al deslizamiento 2D en la pantalla.
Tome el vector y proyecte en la cámara cerca del plano para obtener un vector 3D en el espacio mundial. Pantalla de espacio al espacio mundial .
Luego crea un cuaternión con este vector y multiplícalo en un objeto de juego. Probablemente con un poco de sorbo o transición lerp.
Editar:
Ejemplo de unidad: en el ejemplo de unidad, el estado interno de la rotación de los objetos del juego es un cuaternión, no una matriz. El método transform.rotation genera un cuaternión basado en el vector y el ángulo proporcionados y multiplica ese cuaternión con el cuaternión de rotación de los objetos del juego. Solo genera la matriz de rotación para renderización o física en un punto posterior. Los cuaterniones son aditivos y evitan el bloqueo de los gimbles.
Su código debería verse así:
ArcBall Rotation Opengl Tutorial
fuente
Pude obtener las rotaciones esperadas al girar una matriz de rotación acumulada.
fuente
Su imagen corresponde con su código de Matriz de rotación, al girar el eje x con su rotación y anterior obtiene el eje x local, cuando luego gira el objeto alrededor del cual obtendrá el resultado que muestra en su imagen. Para que la rotación sea lógica desde el punto de vista de los usuarios, desearía rotar el objeto utilizando el eje de coordenadas del mundo.
Si desea que su usuario pueda girar su objeto muchas veces, tendría sentido almacenar su rotación en un cuaternión en lugar de una matriz, con el tiempo múltiples giros (e inexactitudes de coma flotante) harán que la matriz se vea cada vez menos una matriz de rotación, lo mismo sucede en un cuaternión, por supuesto, pero la normalización del cuaternión lo devuelve a una buena rotación.
Simplemente use el quaternion de identidad como valor inicial, y cada vez que el usuario desliza la pantalla, gira su quaternion con el
Quaternion.fromAxisAngle(yAxis, -angleX)
código. Siempre usando (1,0,0,1) para rotaciones x y (0,1,0,1) para rotaciones y.Como no mencionó el idioma ni ningún marco específico, los métodos en Quaternion pueden llamarse algo diferente, por supuesto, y normalizar no es necesario llamar con tanta frecuencia, pero dado que la rotación proviene de que un usuario desliza la pantalla, no lo hará. ralentiza mucho las cosas y de esa manera no hay posibilidad de que el Quaternion se escape de una unidad quaternion.
fuente
myRotation = rotationX.rotate(rotationY).rotate(myRotation).normalize()
no son conmutativos, por lo que el orden en que los hace cuenta. Agregue otro comentario con su marco / idioma si eso no funcionó y lo profundizaré un poco más.