Veo preguntas que surgen con bastante frecuencia que tienen este problema subyacente, pero todas están atrapadas en los detalles de una característica o herramienta determinada. Aquí hay un intento de crear una respuesta canónica a la que podamos referir a los usuarios cuando surja, ¡con muchos ejemplos animados! :)
Digamos que estamos haciendo una cámara en primera persona. La idea básica es que debe guiarse para mirar hacia la izquierda y hacia la derecha, y inclinar para mirar hacia arriba y hacia abajo. Entonces escribimos un poco de código como este (usando Unity como ejemplo):
void Update() {
float speed = lookSpeed * Time.deltaTime;
// Yaw around the y axis using the player's horizontal input.
transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f);
// Pitch around the x axis using the player's vertical input.
transform.Rotate(-Input.GetAxis("Vertical") * speed, 0f, 0f);
}
o tal vez
// Construct a quaternion or a matrix representing incremental camera rotation.
Quaternion rotation = Quaternion.Euler(
-Input.GetAxis("Vertical") * speed,
Input.GetAxis("Horizontal") * speed,
0);
// Fold this change into the camera's current rotation.
transform.rotation *= rotation;
Y funciona principalmente, pero con el tiempo la vista comienza a torcerse. ¡La cámara parece estar girando sobre su eje de giro (z) a pesar de que solo le dijimos que girara en x e y!
Esto también puede suceder si estamos tratando de manipular un objeto frente a la cámara; digamos que es un globo que queremos girar para mirar alrededor:
El mismo problema: después de un tiempo, el polo norte comienza a alejarse hacia la izquierda o la derecha. Estamos aportando información sobre dos ejes, pero estamos obteniendo esta rotación confusa en un tercero. Y sucede si aplicamos todas nuestras rotaciones alrededor de los ejes locales del objeto o los ejes globales del mundo.
En muchos motores también verás esto en el inspector: ¡gira el objeto en el mundo y de repente los números cambian en un eje que ni siquiera tocamos!
Entonces, ¿es esto un error del motor? ¿Cómo le decimos al programa que no queremos que agregue rotación adicional?
¿Tiene algo que ver con los ángulos de Euler? ¿Debería usar Quaternions o Matrices de rotación o Vectores básicos en su lugar?
fuente
Respuestas:
No, esto no es un error del motor o un artefacto de una representación de rotación en particular (también puede ocurrir, pero este efecto se aplica a todos los sistemas que representan rotaciones, incluidos los cuaterniones).
Has descubierto un hecho real sobre cómo funciona la rotación en el espacio tridimensional, y parte de nuestra intuición sobre otras transformaciones como la traducción:
Cuando componimos rotaciones en más de un eje, el resultado que obtenemos no es solo el valor total / neto que aplicamos a cada eje (como podríamos esperar para la traducción). El orden en el que aplicamos las rotaciones cambia el resultado, ya que cada rotación mueve los ejes en los que se aplican las siguientes rotaciones (si gira alrededor de los ejes locales del objeto), o la relación entre el objeto y el eje (si gira alrededor del mundo ejes).
El cambio de las relaciones de los ejes a lo largo del tiempo puede confundir nuestra intuición sobre lo que se supone que debe hacer cada eje. En particular, ciertas combinaciones de rotaciones de guiñada y cabeceo dan el mismo resultado que una rotación de balanceo.
Puede verificar que cada paso gire correctamente sobre el eje que solicitamos: no hay fallas o artefactos en nuestra notación que interfieran o cuestionen nuestra entrada; la naturaleza de rotación esférica (o hiperesférica / cuaternión) solo significa que nuestras transformaciones se "ajustan" alrededor "el uno del otro. Pueden ser ortogonales localmente, para pequeñas rotaciones, pero a medida que se acumulan, descubrimos que no son ortogonales a nivel mundial.
Esto es más dramático y claro para giros de 90 grados como los anteriores, pero los ejes errantes también se arrastran en muchas pequeñas rotaciones, como se demuestra en la pregunta.
¿Entonces qué hacemos al respecto?
Si ya tiene un sistema de rotación pitch-yaw, una de las formas más rápidas de eliminar el giro no deseado es cambiar una de las rotaciones para operar en los ejes de transformación global o padre en lugar de los ejes locales del objeto. De esa manera no puede contaminarse entre los dos: un eje permanece absolutamente controlado.
Aquí está la misma secuencia de pitch-yaw-pitch que se convirtió en un rollo en el ejemplo anterior, pero ahora aplicamos nuestro guiñada alrededor del eje Y global en lugar del objeto
Entonces podemos arreglar la cámara en primera persona con el mantra "Pitch Locally, Yaw Globally":
Si estás combinando tus rotaciones usando la multiplicación, cambiarías el orden izquierdo / derecho de una de las multiplicaciones para obtener el mismo efecto:
(El orden específico dependerá de las convenciones de multiplicación en su entorno, pero izquierda = más global / derecha = más local es una opción común)
Esto es equivalente a almacenar el guiñada total neta y el tono total que desee como variables flotantes, luego aplicar siempre el resultado neto de una vez, construyendo un solo cuaternión o matriz de orientación nueva solo desde estos ángulos (siempre que lo mantenga
totalPitch
sujeto):o equivalente...
Usando esta división global / local, las rotaciones no tienen la oportunidad de combinarse e influenciarse entre sí, porque se aplican a conjuntos de ejes independientes.
La misma idea puede ayudar si es un objeto en el mundo que queremos rotar. Para un ejemplo como el mundo, a menudo queremos invertirlo y aplicar nuestro guiñada localmente (por lo que siempre gira alrededor de sus polos) y lanzar globalmente (por lo que se inclina hacia / lejos de nuestra vista, en lugar de hacia / lejos de Australia , donde sea que apunte ...)
Limitaciones
Esta estrategia híbrida global / local no siempre es la solución correcta. Por ejemplo, en un juego con vuelo / natación en 3D, es posible que desee apuntar hacia arriba / hacia abajo y aún así tener el control total. Pero con esta configuración, golpeará el bloqueo del cardán : su eje de guiñada (global hacia arriba) se vuelve paralelo a su eje de balanceo (local hacia adelante), y no tiene forma de mirar hacia la izquierda o hacia la derecha sin girar.
Lo que puede hacer en casos como este es usar rotaciones locales puras como comenzamos en la pregunta anterior (para que sus controles se sientan igual sin importar dónde esté mirando), lo que inicialmente permitirá que algo de rollo se arrastre, pero luego corregimos por ello.
Por ejemplo, podemos usar rotaciones locales para actualizar nuestro vector "hacia adelante", luego usar ese vector hacia adelante junto con un vector "hacia arriba" de referencia para construir nuestra orientación final. (Usando, por ejemplo, Unity's Quaternion.LookRotation método , o construyendo manualmente una matriz ortonormal a partir de estos vectores) Al controlar el vector hacia arriba, controlamos el giro o giro.
Para el ejemplo de vuelo / natación, querrá aplicar estas correcciones gradualmente con el tiempo. Si es demasiado abrupto, la vista puede tambalearse de manera distractora. En su lugar, puede usar el vector ascendente actual del jugador e insinuarlo hacia la vertical, cuadro por cuadro, hasta que su vista se nivele. Aplicar esto durante un turno a veces puede ser menos nauseabundo que girar la cámara mientras los controles del jugador están inactivos.
fuente
TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)
: esto es equivalente al ejemplo de "todo a la vez Euler" anterior.DESPUÉS de 16 horas de funciones de crianza y rotación lol.
Solo quería que el código tuviera un vacío, básicamente, girando en círculos y también volteando hacia adelante a medida que gira.
O, en otras palabras, el objetivo es rotar el guiñada y el lanzamiento sin ningún efecto en el lanzamiento.
Aquí están los pocos que realmente funcionaron en mi aplicación
En la unidad:
Ahora, crea secuencias de comandos en Visual Studio, configura y termina con esto en Actualización:
Tenga en cuenta que todo se rompió para mí cuando intenté variar el orden de crianza. O si cambio Space.World por el objeto de tono secundario (al padre Yaw no le importa que lo giren a través de Space.Self). Confuso. Definitivamente podría usar algunas aclaraciones sobre por qué tuve que tomar un eje local pero aplicarlo a través del espacio mundial: ¿por qué Space.Self arruina todo?
fuente
me.Rotate(me.right, angle, Space.World)
es lo mismome.Rotate(Vector3.right, angle, Space.Self
y sus transformaciones anidadas son equivalentes a los ejemplos de "Pitch Locally, Yaw Globally" en la respuesta aceptada.