Rotación arbitraria sobre una esfera

12

Estoy codificando una mecánica que permite a un usuario moverse alrededor de la superficie de una esfera. La posición en la esfera se almacena actualmente como thetay phi, donde thetaes el ángulo entre el eje z y la proyección xz de la posición actual (es decir, la rotación alrededor del eje y), y phies el ángulo desde el eje y hasta la posición. Lo expliqué mal, pero es esencialmente theta = yaw,phi = pitch

Vector3 position = new Vector3(0,0,1);
position.X = (float)Math.Sin(phi) * (float)Math.Sin(theta);
position.Y = (float)Math.Sin(phi) * (float)Math.Cos(theta);
position.Z = (float)Math.Cos(phi);
position *= r;

Creo que esto es correcto, sin embargo, podría estar equivocado. Necesito poder moverme en una dirección pseudo bidimensional arbitraria alrededor de la superficie de una esfera en el origen del espacio mundial con radio r. Por ejemplo, sostener Wdebe moverse alrededor de la esfera en una dirección hacia arriba en relación con la orientación del jugador.

Creo que debería estar usando un Quaternion para representar la posición / orientación en la esfera, pero no puedo pensar en la forma correcta de hacerlo. La geometría esférica no es mi fuerte.

Esencialmente, necesito completar el siguiente bloque:

public void Move(Direction dir)
{   
    switch (dir)
    {
        case Direction.Left:
            // update quaternion to rotate left
            break;
        case Direction.Right:   
            // update quaternion to rotate right
            break;
        case Direction.Up:
            // update quaternion to rotate upward
            break;
        case Direction.Down:
            // update quaternion to rotate downward
            break;
    }
}
azz
fuente
¿Qué debería pasar si el jugador llega a los polos? Me di cuenta de que escribiste "hacia arriba", ¿quieres decir literalmente "hacia arriba" (es decir, lejos de la superficie de la esfera), "hacia adelante" o "hacia el polo norte" (los dos últimos son iguales si el jugador no puede cambiar su orientación y "delante de ellos" o "arriba" en la pantalla siempre está al norte)?
Martin Sojka
Quizás estaba mal redactado. El jugador no debe abandonar la superficie de la esfera y no debe ser consciente del eje cardinal. Entonces, cuando te mueves "hacia arriba", te mueves a lo largo de la superficie de la esfera en una dirección hacia arriba en relación con la orientación del jugador. Por ejemplo, si estás en (r, 0,0) y presionas hacia arriba, irás hacia el polo z +, pero si sigues avanzando, debes continuar y continuar.
azz
Aún queda una pregunta: ¿puede el jugador cambiar de orientación (girar "izquierda" y "derecha")?
Martin Sojka
Tal vez un mejor ejemplo de lo que estoy buscando: el jugador que se (1,1,1)mantiene a la izquierda rotaría alrededor de la esfera, pasaría (~1.2,0,~-1.2), luego (-1,-1,-1), luego (~-1.2,0,~1.2)y volvería a (1,1,1).
azz
1
Si tiene la intención de realizar un seguimiento de su puesto thetay, a phimedida que se actualiza su posición, está haciendo su problema innecesariamente complejo. Mucho más fácil simplemente calcular los 2 ejes de rotación de cada cuadro (uno de los cuales (guiñada) nunca cambia) y Vector3.Transormalrededor de la esfera. Esto simplificaría su problema pero hará que se desconecte con phi& theta.
Steve H

Respuestas:

5

En realidad, resulta que no puedes tenerlo "en ambos sentidos": si tu intención es no tener ningún sentido de "orientación absoluta" en la esfera (es decir, si los jugadores no siempre están, por ejemplo, mirando hacia los polos ), deberá tener una noción de orientación del jugador. Esto se debe a que, contrariamente a lo que pueda sugerir la intuición, el movimiento en la esfera no es exactamente como el movimiento en un plano, ni siquiera localmente (bastante); ¡La curvatura intrínseca de la esfera significa que los jugadores pueden tomar acciones que rotarán por sí mismos!

Para el ejemplo más extremo de lo que estoy hablando, imagine que el jugador comienza en un punto del ecuador (por conveniencia, imaginaremos una cara del reloj mapeada en el ecuador desde arriba, y colocaremos al jugador a las 6 en punto ), mirando hacia arriba, es decir, hacia el Polo Norte. Supongamos que el jugador camina hasta el Polo Norte; entonces se enfrentarán directamente hacia el punto de las 12 en punto. Ahora, deje que el jugador se mueva directamente a su derecha, desde el Polo Norte de regreso al ecuador; terminarán en el punto de las 3 en punto, pero porque su orientación no cambia cuando se mueven a la derecha(la idea es que su orientación no cambia sin importar cómo se muevan), aún estarán frente al punto de las 12 en punto, ¡ahora están orientados a lo largo del ecuador! Ahora, déjelos moverse 'hacia atrás' de regreso a su punto inicial (6 en punto); entonces todavía estarán orientados a lo largo del ecuador, por lo que estarán orientados hacia el punto de las 3 en punto, simplemente moviéndose a lo largo de la esfera sin cambiar nunca su orientación 'personal' les ha hecho rotar desde la orientación hacia el polo norte hacia mirando a lo largo del ecuador! En cierto sentido, esta es una elaboración de la vieja broma 'un cazador se mueve una milla al sur, una milla al oeste y luego una milla al norte', pero aquí estamos aprovechando la curvatura de la esfera para efectuar un cambio de dirección. Tenga en cuenta que el mismo efecto todavía ocurre incluso en escalas mucho más pequeñas;

Afortunadamente, los cuaterniones manejan esta situación (como notó usted mismo); Como un cuaternión representa una rotación arbitraria, efectivamente representa un 'punto más orientación' arbitrario en la esfera: imagínese comenzando con una 'triaxis' en el origen y dándole cierta rotación arbitraria, luego moviendo una unidad en cualquier dirección de los ejes rotados ' Puntos del eje Z; un poco de pensamiento lo convencerá de que esto lo lleva a un punto en la esfera de la unidad con alguna 'orientación' (es decir, una disposición de los ejes X e Y de su triaxis), y que puede llegar a cada punto + orientación en el Esfera de unidad de esta manera (solo asigne su eje Z para apuntar a lo largo de la línea desde el origen a través de su punto en la esfera, luego transporte sus triaxes de regreso al origen a lo largo de esa línea). Y lo que es más, Dado que la multiplicación de los cuaterniones corresponde a la composición de las rotaciones, cada una de las operaciones que describe puede representarse multiplicando su 'orientación actual' por un cuaternión elegido adecuadamente: específicamente, dado que el cuaternión (unidad) (qx, qy, qz, qw) significa 'rotar alrededor del eje (qx, qy, qz) por arccos (qw)', luego (dependiendo de su elección específica del sistema de coordenadas, y dejar que c_a sea cos (alpha) y s_a sea sin (alpha)) dos de los tres cuaterniones M_x = (s_a, 0, 0, c_a), M_y = (0, s_a, 0, c_a) y M_z = (0, 0, s_a, c_a) representarán 'rotar (es decir, moverse) en la dirección I 'Actualmente estoy mirando por alfa' y 'girar en una dirección ortogonal a la que estoy mirando actualmente por alfa'. (El tercero de esos cuaterniones representará 'rotar mi personaje sobre su propio eje'Cur_q = M_x * Cur_qsi el jugador presionó hacia arriba, o Cur_q = M_y * Cur_qsi el jugador presionó hacia la derecha (o posiblemente algo así como Cur_q = M_yinv * Cur_qsi el jugador presionó hacia la izquierda, donde M_yinv es el 'inverso' del cuaternión M_y, que representa una rotación en la otra dirección). Tenga en cuenta que debe tener cuidado en qué 'lado' aplica la rotación, ya sea premultiply o postmultiply; para ser sincero, puede ser más fácil resolver eso con prueba y error, probando ambas multiplicaciones y viendo cuál funciona.

Pasar de su cuaternión actualizado a un punto en la esfera (y a una orientación de su personaje) también es relativamente sencillo: según la correspondencia del último párrafo, todo lo que tiene que hacer es usar su cuaternión en los vectores de base (1, 0,0), (0,1,0) y (0,0,1) de su marco a través de la operación 'rotar vector por cuaternión' v → qvq -1 (donde las multiplicaciones aquí son cuaterniones se multiplican e identificamos el vector v = (x, y, z) con el 'cuaternión degenerado' (x, y, z, 0)). Por ejemplo, la posición en la esfera de la unidad se obtiene simplemente transformando el vector z: pos = (qx, qy, qz, qw) * (0, 0, 1, 0) * (-qx, -qy, -qz, qw) = (qx, qy, qz, qw) * (qy, -qx, qw, qz) = (2 (qy * qw + qz * qx), 2 (qz * qy-qw * qx), (qz ^ 2 + qw ^ 2) - (qx ^ 2 + qy ^ 2), 0), entonces(2(qy*qw+qz*qx), 2(qz*qy-qw*qx), (qz^2+qw^2)-(qx^2+qy^2))serían las coordenadas del usuario 'transformado' en la esfera de la unidad (y para obtener las coordenadas en una esfera arbitraria, por supuesto, simplemente las multiplicaría por el radio de la esfera); cálculos similares funcionan para los otros ejes, para definir, por ejemplo, la dirección de orientación del usuario.

Steven Stadnicki
fuente
Precisamente lo que quiero lograr. Simplemente no podía pensar en la forma correcta de sacar una posición del cuaternión de orientación. Utilizando lo que me ha proporcionado, puedo escribir el Move()procedimiento, pero para obtener el eje normalizado (es decir, mi posición), ¿lo tomaría (sin(qx),sin(qy),sin(qw)) * r?
azz
@Der no exactamente: actualizaré mi publicación con los detalles, pero la versión corta es que usas tu quaternion para transformar un vector unitario, por ejemplo (0,0,1), por el v -> qvq <sup> habitual -1 </sup> operación; el hecho de que estés transformando un vector simple significa que (naturalmente) hay un atajo aquí, pero las coordenadas finales son cuadráticas en los valores de tu cuaternión, no lineales.
Steven Stadnicki
1

Creo que quieres algo similar a esto http://www.youtube.com/watch?v=L2YRZbRSD1k

Desarrollé eso para un gamejam de 48h ... puedes descargar el código aquí ... http://archive.globalgamejam.org/2011/evil-god

Utilicé algo similar a tu código para obtener las coordenadas 3D ... pero roté el planeta y el jugador estaba en la misma posición, creo que estás interesado en el movimiento de la criatura, es esto:

    // To add movement
    protected override void LocalUpdate(float seconds)
    {
        Creature.Alfa += Direction.X * seconds * Speed;
        Creature.Beta += Direction.Y * seconds * Speed;            
    }


    // To calculate position
       World.Planet.GetCartesian(Alfa, Beta, out Position); // as you do
       Matrix PositionMatrix = Matrix.CreateTranslation(Position) * World.Planet.RotationMatrix;           
       LastPositionAbsolute = PositionAbsolute;
       Vector3 Up = PositionAbsolute = Vector3.Transform(Vector3.Zero, PositionMatrix);           
       Up.Normalize();
       // This is to add and offset to the creature model position
       PositionAbsolute += Up * 8;  
      // calculate new forward vector if needed

       if ((PositionAbsolute - LastPositionAbsolute).Length() > 0.1f) {
           Forward = PositionAbsolute - LastPositionAbsolute;
           Forward.Normalize();
       }

       // Calculate the world transform with position, forward vector and up vector
       Matrix LocalWorld = Matrix.CreateWorld(PositionAbsolute, Forward, Up); 

       Transform = Matrix.CreateScale(Scale * ScaleFactor) * LocalWorld;
Blau
fuente