Rotation arbitraire d'une sphère

12

Je code un mécanicien qui permet à un utilisateur de se déplacer sur la surface d'une sphère. La position sur la sphère est actuellement stockée sous la forme thetaet phi, où thetaest l'angle entre l'axe z et la projection xz de la position actuelle (c'est-à-dire la rotation autour de l'axe y), et phiest l'angle entre l'axe y et la position. Je lui ai expliqué que mal, mais il est essentiellement 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;

Je crois que c'est exact, mais je peux me tromper. J'ai besoin de pouvoir me déplacer dans une direction pseudo-bidimensionnelle arbitraire autour de la surface d'une sphère à l'origine de l'espace mondial avec rayon r. Par exemple, la tenue Wdevrait se déplacer autour de la sphère vers le haut par rapport à l'orientation du joueur.

Je crois que je devrais utiliser un Quaternion pour représenter la position / orientation sur la sphère, mais je ne peux pas penser à la bonne façon de le faire. La géométrie sphérique n'est pas mon fort.

Essentiellement, je dois remplir le bloc suivant:

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
la source
Que doit-il se passer si le joueur atteint les pôles? J'ai remarqué que vous avez écrit "vers le haut", voulez-vous dire littéralement "vers le haut" (c'est-à-dire loin de la surface de la sphère), "tout droit" ou "vers le pôle nord" (les deux derniers sont les mêmes si le joueur ne peut pas changer leur orientation et "devant eux" ou "en haut" à l'écran est toujours le nord)?
Martin Sojka
C'était peut-être mal formulé. Le joueur ne doit pas quitter la surface de la sphère et ne doit pas être conscient de l'axe cardinal. Ainsi, lorsque vous vous déplacez "vers le haut", vous vous déplacez le long de la surface de la sphère vers le haut par rapport à l'orientation du joueur. Par exemple, si vous êtes à (r, 0,0) et que vous poussez vers le haut, vous irez vers le pôle z +, mais si vous continuez, vous devriez faire le tour et continuer.
azz
Une question demeure: le joueur peut-il changer d'orientation (tourner "à gauche" et "à droite")?
Martin Sojka
Peut-être un meilleur exemple de ce que je veux faire: le joueur qui (1,1,1)tient à gauche tournerait autour de la sphère, passant à travers (~1.2,0,~-1.2), puis (-1,-1,-1), puis (~-1.2,0,~1.2)et retour à (1,1,1).
azz
1
Si vous avez l'intention de toujours garder une trace de votre position thetaet phide la mettre à jour, vous compliquez inutilement votre problème. Il est beaucoup plus facile de calculer simplement les 2 axes de rotation de chaque image (dont l'un (lacet) ne change jamais) et Vector3.Transormautour de la sphère. Cela simplifierait votre problème mais vous ferait vous déconnecter avec phi& theta.
Steve H

Réponses:

5

En fait, il s'avère que vous ne pouvez pas l'avoir dans les deux sens: si votre intention est de ne pas avoir le sens d'une "orientation absolue" sur la sphère (c'est-à-dire si les joueurs ne sont pas toujours par exemple face aux pôles) ), vous devrez alors avoir une notion d'orientation du joueur. En effet, contrairement à ce que pourrait suggérer l'intuition, le mouvement sur la sphère n'est pas exactement comme le mouvement sur un plan, pas même localement (tout à fait); la courbure intrinsèque de la sphère signifie que les joueurs peuvent entreprendre des actions qui se tourneront!

Pour l'exemple le plus extrême de ce dont je parle, imaginez que le joueur commence à un point de l'équateur (pour plus de commodité, nous imaginerons un cadran d'horloge mappé sur l'équateur par le haut et placer le joueur à 6 heures ), face «vers le haut», c'est-à-dire vers le pôle Nord. Supposons que le joueur marche jusqu'au pôle Nord; alors ils feront face directement au point de 12 heures. Maintenant, laissez le joueur se déplacer directement à sa droite, du pôle Nord à l'équateur; ils se retrouveront à 3 heures - mais parce que leur orientation ne change pas lorsqu'ils se déplacent vers la droite(l'idée est que leur face ne change pas, peu importe comment ils se déplacent), ils seront toujours face au point de 12 heures - ils font maintenant face le long de l'équateur! Maintenant, laissez-les revenir «en arrière» à leur point de départ (6 heures); alors ils seront toujours face à l'équateur, donc ils seront face au point de 3 heures - le simple fait de se déplacer le long de la sphère sans jamais changer leur orientation "personnelle" les a fait tourner de face au pôle nord à face à l'équateur! Dans un sens, c'est une élaboration de la vieille plaisanterie `` un chasseur se déplace d'un mile au sud, d'un mile à l'ouest puis d'un mile au nord '' - mais ici, nous profitons de la courbure de la sphère pour effectuer un changement de direction. Notez que le même effet se produit même à des échelles beaucoup plus petites;

Heureusement, les quaternions gèrent (comme vous l'avez noté vous-même) cette situation; puisqu'un quaternion représente une rotation arbitraire, il représente effectivement un `` point plus une orientation '' arbitraire sur la sphère: imaginez commencer par un `` triaxis '' à l'origine et lui donner une rotation arbitraire, puis déplacer une unité dans la direction des axes tournés '' Points de l'axe Z; une petite réflexion vous convaincra que cela vous amène à un point sur la sphère unitaire avec une certaine "orientation" (c'est-à-dire une certaine disposition des axes X et Y de votre triaxie), et que vous pouvez accéder à chaque point + orientation sur la de cette façon (affectez simplement votre axe Z à un point le long de la ligne de l'origine à votre point sur la sphère, puis ramenez vos triaxies à l'origine le long de cette ligne). Quoi de plus, puisque la multiplication des quaternions correspond à la composition des rotations, chacune des opérations que vous décrivez peut être représentée en multipliant votre `` orientation actuelle '' par un quaternion choisi de manière appropriée: en particulier, puisque le quaternion (unité) (qx, qy, qz, qw) signifie 'tourner autour de l'axe (qx, qy, qz) par arccos (qw)', puis (selon votre choix spécifique de système de coordonnées, et en laissant c_a être cos (alpha) et s_a être sin (alpha)) deux des trois quaternions M_x = (s_a, 0, 0, c_a), M_y = (0, s_a, 0, c_a) et M_z = (0, 0, s_a, c_a) représenteront 'tourner (c'est-à-dire se déplacer) dans la direction I 'suis actuellement face à l'alpha' et 'tourne dans une direction orthogonale à celle à laquelle je suis actuellement confronté par l'alpha'. (Le troisième de ces quaternions représentera `` tourner mon personnage autour de son propre axe ''Cur_q = M_x * Cur_qsi le joueur a appuyé vers le haut, ou Cur_q = M_y * Cur_qsi le joueur a appuyé vers la droite (ou peut-être quelque chose comme Cur_q = M_yinv * Cur_qsi le joueur a appuyé vers la gauche, où M_yinv est l'inverse du quaternion M_y, représentant une rotation dans l'autre sens). Notez que vous devez faire attention à quel «côté» vous appliquez la rotation, que ce soit pour prémultiplier ou postmultiplier; pour être franc, il peut être plus facile de résoudre ce problème par essais et erreurs, en essayant les deux multiplications et en voyant ce qui fonctionne.

Passer de votre quaternion mis à jour à un point sur la sphère (et à une orientation de votre personnage) est aussi relativement simple: par la correspondance du dernier paragraphe, tout ce que vous avez à faire est d'utiliser votre quaternion sur les vecteurs de base (1, 0,0), (0,1,0) et (0,0,1) de votre image via l'opération 'rotation du vecteur par quaternion' v → qvq -1 (où les multiplications ici sont des multiplications de quaternion et nous identifions le vecteur v = (x, y, z) avec le 'quaternion dégénéré' (x, y, z, 0)). Par exemple, la position sur la sphère unitaire est obtenue en transformant simplement le vecteur 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), donc(2(qy*qw+qz*qx), 2(qz*qy-qw*qx), (qz^2+qw^2)-(qx^2+qy^2))serait les coordonnées de l'utilisateur «transformé» sur la sphère unitaire (et pour obtenir les coordonnées sur une sphère arbitraire, bien sûr, vous devez simplement les multiplier par le rayon de la sphère); des calculs similaires fonctionnent pour les autres axes, pour définir, par exemple, la direction d'orientation de l'utilisateur.

Steven Stadnicki
la source
C'est précisément ce que je veux réaliser. Je ne pouvais tout simplement pas penser à la bonne façon d'obtenir une position hors du quaternion d'orientation. En utilisant ce que vous avez fourni, je peux écrire la Move()procédure, mais pour obtenir l'axe normalisé (c'est-à-dire ma position), devrais-je simplement prendre (sin(qx),sin(qy),sin(qw)) * r?
azz
@ Ne pas exactement - je mettrai à jour mon article avec les détails, mais la version courte est que vous utilisez votre quaternion pour transformer un vecteur unitaire, par exemple (0,0,1), par l'habituel v -> qvq <sup> -1 </sup> opération; le fait que vous transformiez un simple vecteur signifie (naturellement) qu'il y a un raccourci ici, mais les coordonnées finales sont quadratiques dans les valeurs de votre quaternion, pas linéaires.
Steven Stadnicki
1

Je pense que vous voulez quelque chose de similaire à ceci http://www.youtube.com/watch?v=L2YRZbRSD1k

J'ai développé cela pour un gamejam de 48h ... vous pouvez télécharger le code ici ... http://archive.globalgamejam.org/2011/evil-god

J'ai utilisé quelque chose de similaire à votre code pour obtenir les coordonnées 3D ... mais j'ai fait pivoter la planète et le joueur était dans la même position, je pense que vous êtes intéressé par le mouvement des créatures, est-ce:

    // 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
la source