Multiplicación
Al menos en términos de la implementación de Unity de Quaternions, el orden de multiplicación descrito en la pregunta no es correcto. Esto es importante porque la rotación 3D no es conmutativa .
Entonces, si quiero rotar un objeto rotationChange
comenzando desde él currentOrientation
, lo escribiría así:
Quaternion newOrientation = rotationChange * currentOrientation;
(es decir. Las transformaciones se apilan hacia la izquierda, igual que la convención de matriz de Unity. La rotación más a la derecha se aplica primero / en el extremo "más local")
Y si quisiera transformar una dirección o un vector de desplazamiento mediante una rotación, lo escribiría así:
Vector3 rotatedOffsetVector = rotationChange * currentOffsetVector;
(Unity generará un error de compilación si haces lo contrario)
Mezcla
Para la mayoría de los casos, puedes salirte con la rotación de Lerping Esto se debe a que el ángulo utilizado "debajo del capó" en un cuaternión es la mitad del ángulo de rotación, lo que lo hace sustancialmente más cercano a la aproximación lineal de Lerp que algo así como una Matriz (¡que en general no Lerp bien!). Mira alrededor de 40 minutos en este video para obtener más explicaciones .
El único caso en el que realmente necesita Slerp es cuando necesita una velocidad constante en el tiempo, como la interpolación entre fotogramas clave en una línea de tiempo de animación. Para los casos en los que solo le importa que una salida sea intermedia entre dos entradas (como mezclar capas de una animación), generalmente Lerp sirve bastante bien.
¿Qué más?
El producto de puntos de dos unidades de cuaterniones da el coseno del ángulo entre ellos, por lo que puede usar el producto de puntos como una medida de similitud si necesita comparar rotaciones. Sin embargo, esto es un poco oscuro, por lo que para un código más legible a menudo usaría Quaternion.Angle (a, b) en su lugar, que expresa más claramente que estamos comparando ángulos, en unidades familiares (grados).
Estos tipos de métodos de conveniencia que Unity proporciona para Quaternions son súper útiles. En casi todos los proyectos uso este al menos algunas veces :
Quaternion.LookRotation(Vector3 forward, Vector3 up)
Esto genera un cuaternión que:
- gira el eje z + local para apuntar exactamente a lo largo del
forward
argumento vectorial
- gira el eje local y + para apuntar lo más cerca posible del
up
argumento vectorial, si se proporciona, o (0, 1, 0)
si se omite
La razón por la que "arriba" solo se "acerca lo más posible" es que el sistema está sobredeterminado. Enfrentando z + a forward
usa hasta dos grados de libertad (es decir, guiñada y cabeceo), por lo que solo nos queda un grado de libertad (rodar).
A menudo encuentro que quiero algo con las propiedades de exactitud opuestas: quiero que y + local apunte exactamente a lo largo up
, y z + local se acerque lo más posible a forward
la libertad restante.
Esto surge, por ejemplo, cuando trato de formar un marco de coordenadas relativo a la cámara para la entrada de movimiento: quiero que mi dirección local hacia arriba permanezca perpendicular al piso o superficie inclinada normal, por lo que mi entrada no intenta hacer un túnel del personaje en el terreno o levitarlos fuera de él.
También puede obtener esto si desea que la torreta de un tanque mire hacia un objetivo, sin despegarse del cuerpo del tanque cuando apunta hacia arriba / abajo.
Podemos construir nuestra propia función de conveniencia para hacer esto, utilizando LookRotation
para el trabajo pesado:
Quaternion TurretLookRotation(Vector3 approximateForward, Vector3 exactUp)
{
Quaternion rotateZToUp = Quaternion.LookRotation(exactUp, -approximateForward);
Quaternion rotateYToZ = Quaternion.Euler(90f, 0f, 0f);
return rotateZToUp * rotateYToZ;
}
Aquí primero rotamos local y + a z +, y local z + a y-.
Luego, rotamos el nuevo z + hacia nuestra dirección hacia arriba (de modo que el resultado neto es local y + apunta directamente a lo largo exactUp
), y el nuevo y + lo más cerca posible a la dirección negada hacia adelante (por lo que el resultado neto es local z + puntos lo más cerca posible a lo largo approximateForward
)
Otro método práctico de conveniencia es Quaternion.RotateTowards
, que a menudo uso así:
Quaternion newRotation = Quaternion.RotateTowards(
oldRotation,
targetRotation,
maxDegreesPerSecond * Time.deltaTime
);
Esto nos permite acercarnos targetRotation
a una velocidad constante y controlable, independientemente de la velocidad de fotogramas, importante para las rotaciones que afectan el resultado / la imparcialidad de la mecánica del juego (como girar el movimiento de un personaje o tener una torreta en el jugador). El ingenuo Lerping / Slerping en esta situación puede conducir fácilmente a casos en los que el movimiento se vuelve más ágil a velocidades de cuadro altas, lo que afecta el equilibrio del juego. (Eso no quiere decir que estos métodos sean incorrectos: hay formas de usarlos correctamente sin cambiar la equidad, solo requiere cuidado. RotateTowards
Ofrece un atajo conveniente que se encarga de esto por nosotros)
n
diferentes orientaciones (actitudes, poses, etc.). Luego puede promediarlos usando pesos, generalizando efectivamente slerp / lerp. También puede convertir un cuaternión en un rotor, lo que equivale a aplicar una velocidad angular durante un cierto período de tiempo a un cuerpo rígido. Por lo tanto, también puede describir la integración de la velocidad angular con cuaterniones. También puede estimar cuán diferentes son las dos orientaciones (calcule la longitud del arco atravesado por los dos cuaterniones en la hiperesfera).