Rotazione arbitraria di una sfera


12

Sto codificando un meccanico che consente a un utente di muoversi sulla superficie di una sfera. La posizione sulla sfera è attualmente memorizzata come thetae phi, dove thetaè l'angolo tra l'asse z e la proiezione xz della posizione corrente (cioè rotazione attorno all'asse y), ed phiè l'angolo dall'asse y alla posizione. Ho spiegato che male, ma è essenzialmente 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;

Credo che questo sia accurato, tuttavia potrei sbagliarmi. Devo essere in grado di muovermi in una pseudo direzione bidimensionale arbitraria attorno alla superficie di una sfera all'origine dello spazio mondiale con raggio r. Ad esempio, la tenuta Wdovrebbe spostarsi attorno alla sfera in una direzione verso l'alto rispetto all'orientamento del giocatore.

Credo che dovrei usare un Quaternion per rappresentare la posizione / orientamento sulla sfera, ma non riesco a pensare al modo corretto di farlo. La geometria sferica non è il mio punto di forza.

In sostanza, devo riempire il seguente blocco:

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

Cosa dovrebbe succedere se il giocatore raggiunge i poli? Ho notato che hai scritto "verso l'alto", intendi letteralmente "verso l'alto" (cioè lontano dalla superficie della sfera), "dritto" o "verso il polo nord" (gli ultimi due sono gli stessi se il giocatore non può cambiare il loro orientamento e "davanti a loro" o "su" sullo schermo è sempre il nord)?
Martin Sojka,

Forse era scritto male. Il giocatore non deve lasciare la superficie della sfera e non deve essere consapevole dell'asse cardinale. Quindi quando ti muovi "su", ti muovi lungo la superficie della sfera in una direzione verso l'alto rispetto all'orientamento del giocatore. ad esempio, se ci si trova in (r, 0,0) e si preme verso l'alto, si andrà verso il polo z +, ma se si continua, si dovrebbe andare avanti e continuare.
azz,

Rimane ancora una domanda: il giocatore può cambiare l'orientamento (ruotare "a sinistra" e "a destra")?
Martin Sojka,

Forse un esempio migliore di ciò che sto cercando: il giocatore che (1,1,1)tiene premuto a sinistra ruoterebbe attorno alla sfera, passando (~1.2,0,~-1.2), quindi (-1,-1,-1), poi (~-1.2,0,~1.2)e poi di nuovo (1,1,1).
azz

1
Se hai intenzione di tenere sempre traccia di thetae phimentre i tuoi aggiornamenti di posizione, stai rendendo il problema inutilmente complesso. Molto più semplice calcolare semplicemente i 2 assi di rotazione per ciascun fotogramma (uno dei quali (imbardata) non cambia mai) e Vector3.Transormattorno alla sfera. Ciò semplificherebbe il tuo problema ma ti farà disconnettere con phi& theta.
Steve H,

Risposte:


5

In realtà, si scopre che non si può avere "in entrambi i modi": se la tua intenzione è di non avere alcun senso di "orientamento assoluto" sulla sfera (cioè, se i giocatori non sono sempre ad esempio rivolti verso i poli ), quindi dovrai avere un'idea dell'orientamento del giocatore. Questo perché, contrariamente a quanto l'intuizione potrebbe suggerire, il movimento sulla sfera non è esattamente come il movimento su un piano, nemmeno localmente (abbastanza); la curvatura intrinseca della sfera significa che i giocatori possono compiere azioni che ruotano da sole!

Per l'esempio più estremo di ciò di cui sto parlando, immagina che il giocatore inizi in un punto sull'equatore (per comodità immagineremo un quadrante dell'orologio mappato sull'equatore dall'alto, e mettiamo il giocatore alle 6 in punto ), rivolto "in alto", ovvero verso il Polo Nord. Supponiamo che il giocatore cammini fino al Polo Nord; quindi saranno rivolti direttamente verso il punto 12. Ora lascia che il giocatore si muova direttamente alla sua destra, dal Polo Nord all'equatore; finiranno al punto 3 - ma perché il loro fronte non cambia quando si muovono a destra(l'idea è che il loro orientamento non cambia, indipendentemente da come si muovono), saranno comunque rivolti verso il punto delle 12 - ora sono rivolti lungo l'equatore! Ora, lascia che si spostino 'indietro' al loro punto di partenza (ore 6); poi saranno ancora rivolti verso l'equatore, quindi saranno rivolti verso il punto 3: spostarsi lungo la sfera senza mai cambiare il loro orientamento "personale" li ha fatti ruotare dal fronte verso il polo nord di fronte all'equatore! In un certo senso, questa è un'elaborazione del vecchio scherzo "un cacciatore si sposta di un miglio a sud, un miglio a ovest e poi un miglio a nord", ma qui stiamo sfruttando la curvatura della sfera per effettuare un cambio di direzione. Si noti che lo stesso effetto si verifica anche su scale molto più piccole;

Fortunatamente, i quaternioni (come hai notato te stesso) gestiscono questa situazione; poiché un quaternione rappresenta una rotazione arbitraria, rappresenta effettivamente un "punto più orientamento" arbitrario sulla sfera: immagina di iniziare con una "triaxis" all'origine e di dargli una rotazione arbitraria, quindi di spostare un'unità in qualunque direzione gli assi ruotati " Punti dell'asse Z; un piccolo pensiero ti convincerà che questo ti porta ad un punto sulla sfera unitaria con un po 'di "orientamento" (cioè una disposizione degli assi X e Y della tua triaxis), e che puoi arrivare ad ogni punto + orientamento sulla unità sfera in questo modo (basta assegnare il proprio asse Z per puntare lungo la linea dall'origine attraverso il punto sulla sfera, quindi trasportare i triax all'origine lungo quella linea). Cosa c'è di più, poiché la moltiplicazione dei quaternioni corrisponde alla composizione delle rotazioni, ciascuna delle operazioni che descrivi può essere rappresentata moltiplicando il tuo "orientamento corrente" per un quaternione scelto in modo appropriato: in particolare, dal quaternione (unità) (qx, qy, qz, qw) significa "ruota attorno all'asse (qx, qy, qz) di arccos (qw)", quindi (a seconda della specifica scelta del sistema di coordinate, e lasciando che c_a sia cos (alpha) e s_a sin (alpha)) due dei tre quaternioni M_x = (s_a, 0, 0, c_a), M_y = (0, s_a, 0, c_a) e M_z = (0, 0, s_a, c_a) rappresenteranno 'ruota (cioè sposta) nella direzione I 'attualmente sto affrontando con l'alfa' e 'ruota in una direzione ortogonale a quella che sto attualmente affrontando con l'alfa'. (Il terzo di quei quaternioni rappresenterà "ruota il mio personaggio attorno al proprio asse"Cur_q = M_x * Cur_qse il giocatore ha premuto, o Cur_q = M_y * Cur_qse il giocatore ha premuto a destra (o forse qualcosa del genere Cur_q = M_yinv * Cur_qse il giocatore ha premuto a sinistra, dove M_yinv è l'inverso del M_y quaternion, che rappresenta una rotazione nell'altro senso). Nota che devi stare attento su quale "parte" applichi la rotazione, sia che si debba pre-moltiplicare o post-moltiplicare; per essere sinceri, potrebbe essere più semplice risolverlo con tentativi ed errori, provando entrambe le moltiplicazioni e vedendo quale funziona.

Passare dal tuo quaternione aggiornato a un punto sulla sfera (e ad un orientamento del tuo personaggio) è anche relativamente semplice: dalla corrispondenza dell'ultimo paragrafo, tutto ciò che devi fare è usare il tuo quaternione sui vettori di base (1, 0,0), (0,1,0) e (0,0,1) del fotogramma tramite l'operazione 'ruota vettore per quaternione' v → qvq -1 (dove le moltiplicazioni qui sono moltiplicazioni di quaternione e identifichiamo il vettore v = (x, y, z) con il 'quaternione degenerato' (x, y, z, 0)). Ad esempio, la posizione sulla sfera dell'unità viene ottenuta semplicemente trasformando il vettore 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), quindi(2(qy*qw+qz*qx), 2(qz*qy-qw*qx), (qz^2+qw^2)-(qx^2+qy^2))sarebbero le coordinate dell'utente "trasformato" sulla sfera unitaria (e per ottenere le coordinate su una sfera arbitraria, ovviamente, dovresti semplicemente moltiplicarle per il raggio della sfera); calcoli simili funzionano per gli altri assi, per definire ad esempio la direzione di orientamento dell'utente.


Proprio quello che voglio ottenere. Non riuscivo a pensare al modo corretto di ottenere una posizione fuori dal quaternione di orientamento. Utilizzando ciò che mi hai fornito, posso scrivere la Move()procedura, ma per ottenere l'asse normalizzato (cioè la mia posizione), dovrei semplicemente prendere (sin(qx),sin(qy),sin(qw)) * r?
azz

@ Non esattamente - Aggiornerò il mio post con i dettagli, ma la versione breve è che usi il tuo quaternione per trasformare un vettore di unità, ad es. (0,0,1), dal solito v -> qvq <sup> -1 </sup> operazione; il fatto che stai trasformando un vettore semplice significa che (naturalmente) qui c'è una scorciatoia, ma le coordinate finali sono quadratiche nei valori del tuo quaternione, non lineari.
Steven Stadnicki,

1

Penso che tu voglia qualcosa di simile a questo http://www.youtube.com/watch?v=L2YRZbRSD1k

L'ho sviluppato per un gamejam di 48 ore ... puoi scaricare qui il codice ... http://archive.globalgamejam.org/2011/evil-god

Ho usato qualcosa di simile al tuo codice per ottenere le coordinate 3D ... ma ho ruotato il pianeta e il giocatore era nella stessa posizione, penso che tu sia interessato al movimento della creatura, è questo:

    // 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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.