Comment faire pivoter un objet autour d'axes alignés sur le monde?

15

J'ai un Vector3 qui a un angle euler pour chaque axe.

Habituellement, lorsque je veux créer une matrice de rotation, j'utilise des fonctions telles que D3DXMatrixRotationX en passant l'angle respectif de mon vecteur de rotation ci-dessus et multiplie les matrices (ZXY) pour créer la matrice de rotation globale qui est utilisée pour former la matrice de transformation d'objet complète.

Cependant, cette méthode produira un ensemble de rotations dans l'espace objet. C'est-à-dire que le passage d'un vecteur de (90, 0, 90) dans ma méthode créera une rotation dans l'espace mondial de (90, 90, 0).

Existe-t-il un moyen de toujours garantir que chaque composant de mon vecteur de rotation entraîne une rotation autour des axes respectifs alignés sur l'espace mondial?

ÉDITER:

Ceci est une animation de ce qui se passe actuellement - je veux un moyen de tourner autour des axes bleus, pas du rouge.

Euler Angles

EDIT 2:

Juste pour noter que je ne cherche pas une solution impliquant des angles d'Euler, mais simplement une manière dont je peux représenter une transformation de rotations multiples autour des axes mondiaux.

Syntac_
la source
Quel est le problème avec simplement appeler les fonctions differnet trois fois et filtrer les parties des vecteurs que vous ne voulez pas (en les mettant à 0 avant d'appeler la fonction)? Sinon, je ne suis pas sûr de ce que vous essayez de réaliser.
TravisG
Filtrer quoi? J'appelle les 3 fonctions distinctes, puis je les multiplie pour créer la matrice de transformation. Cette archieves cependant une rotation locale.
Syntac_
Voulez-vous des angles d'Euler ou une rotation autour des axes mondiaux? Notez que par la définition des angles d'Euler (par exemple en.wikipedia.org/wiki/Euler_angles ), seul l'angle alpha est strictement autour d'un axe mondial. Les deux autres angles sont relatifs à des axes inclinés qui ne coïncident pas nécessairement avec les axes mondiaux.
DMGregory
1
En utilisant les angles d'Euler, vous multipliez les trois matrices de rotation avant de les appliquer ensuite sur le sommet. Si M, N, O sont les matrices de rotation, l'opération résultante est MNO v. Ce que j'ai proposé est d'appliquer chaque matrice séparément: v1 = O v0, puis v2 = N v1 et enfin v3 = M v2. De cette façon, chaque vi sera en coordonnées universelles et il vous suffit d'utiliser une matrice de rotation pour l'axe actuel en coordonnées universelles également.
dsilva.vinicius
3
@ dsilva.vinicius Vos transformations séparées sont exactement les mêmes que celles combinées, ou pour le dire autrement: MNO v == M * (N * (O v))
GuyRT

Réponses:

1

Sur la base de vos commentaires, il semble que vous stockiez l'orientation de l'objet sous la forme d'un ensemble d'angles d'Euler et que vous diminuez / décrivez les angles lorsque le joueur fait pivoter l'objet. Autrement dit, vous avez quelque chose comme ce pseudocode:

// in player input handling:
if (axis == AXIS_X) object.angleX += dir;
else if (axis == AXIS_Y) object.angleY += dir;
else if (axis == AXIS_Z) object.angleZ += dir;

// in physics update and/or draw code:
matrix = eulerAnglesToMatrix(object.angleX, object.angleY, object.angleZ);

Comme le note Charles Beattie , parce que les rotations ne font pas la navette, cela ne fonctionnera pas comme prévu à moins que le joueur fasse pivoter l'objet dans le même ordre dans lequel il eulerAnglesToMatrix()applique les rotations.

En particulier, considérez la séquence de rotations suivante:

  1. faire pivoter l'objet de x degrés autour de l'axe X;
  2. faire pivoter l'objet de y degrés autour de l'axe Y;
  3. faire pivoter l'objet de - x degrés autour de l'axe X;
  4. faire pivoter l'objet de - y degrés autour de l'axe Y.

Dans la représentation naïve de l'angle d'Euler, telle qu'implémentée dans le pseudocode ci-dessus, ces rotations s'annuleront et l'objet reviendra à son orientation d'origine. Dans le monde réel, cela ne se produit pas - si vous ne me croyez pas, prenez un dé à six faces ou un cube de Rubik, laissez x = y = 90 °, et essayez-le vous-même!

La solution, comme vous le notez dans votre propre réponse , consiste à stocker l'orientation de l'objet en tant que matrice de rotation (ou quaternion) et à mettre à jour cette matrice en fonction des entrées de l'utilisateur. Autrement dit, au lieu du pseudocode ci-dessus, vous feriez quelque chose comme ceci:

// in player input handling:
if (axis == AXIS_X) object.orientation *= eulerAnglesToMatrix(dir, 0, 0);
else if (axis == AXIS_Y) object.orientation *= eulerAnglesToMatrix(0, dir, 0);
else if (axis == AXIS_Z) object.orientation *= eulerAnglesToMatrix(0, 0, dir);

// in physics update and/or draw code:
matrix = object.orientation;  // already in matrix form!

(Techniquement, comme toute matrice de rotation ou quaternion peut être représenté comme un ensemble d'angles d'Euler, il est possible de les utiliser pour stocker l'orientation de l'objet. Mais la règle physiquement correcte pour combiner deux rotations séquentielles, chacune représentée sous forme d'angles d'Euler, en une seule rotation est plutôt compliqué et revient essentiellement à convertir les rotations en matrices / quaternions, à les multiplier, puis à reconvertir le résultat en angles d'Euler.)

Ilmari Karonen
la source
Oui, vous avez raison, c'était la solution. Je pense que c'est légèrement mieux que la réponse de concept3d car il donne l'impression qu'un quaternion est nécessaire mais ce n'est pas vrai. Tant que je stockais la rotation actuelle sous forme de matrice et non les trois angles d'Euler, c'était bien.
Syntac_
15

Le problème avec les rotations, c'est que, la plupart des gens le pensent en termes d'angles d'Euler, car ils sont faciles à comprendre.

Pourtant, la plupart des gens oublient que les angles d'Euler sont trois angles séquentiels . Cela signifie que la rotation autour du premier axe rendra la rotation suivante relative à la première rotation d'origine, vous ne pouvez donc pas faire pivoter indépendamment un vecteur autour de chacun des 3 axes à l'aide des angles d'Euler.

Cela se traduit directement en matrices lorsque vous multipliez deux matrices, vous pouvez penser à cette multiplication comme transformant une matrice en l'espace de l'autre matrice.

Cela est censé se produire avec 3 rotations séquentielles, même lorsque vous utilisez des quaternions.

entrez la description de l'image ici

Je tiens à souligner le fait que les quaternions ne sont pas une solution pour le verrouillage de vrille. En fait, le verrouillage de vrille se produira toujours si vous représentez les angles d'Euler à l'aide de quaternions. Le problème n'est pas la représentation, le problème est les 3 étapes séquentielles.

La solution?

La solution pour faire tourner un vecteur autour de 3 axes indépendamment est de combiner en un seul axe et un seul angle, de cette façon, vous pouvez vous débarrasser de l'étape où vous devez faire une multiplication séquentielle. Cela se traduira effectivement par:

Ma matrice de rotation représente le résultat de la rotation autour de X et Y et Z.

plutôt que l'interprétation d'Euler de

Ma matrice de rotation représente la rotation autour de X puis Y puis Z.

Pour clarifier cela, je vais citer le théorème de rotation d'Euler de wikipedia:

Selon le théorème de rotation d'Euler, toute rotation ou séquence de rotations d'un corps rigide ou d'un système de coordonnées autour d'un point fixe équivaut à une seule rotation d'un angle donné θ autour d'un axe fixe (appelé axe d'Euler) qui traverse le point fixe. L'axe d'Euler est généralement représenté par un vecteur unitaire u →. Par conséquent, toute rotation en trois dimensions peut être représentée comme une combinaison d'un vecteur u → et d'un scalaire θ. Les quaternions donnent un moyen simple de coder cette représentation axe-angle en quatre nombres et d'appliquer la rotation correspondante à un vecteur de position représentant un point par rapport à l'origine dans R3.

Notez que la multiplication de 3 matrices représentera toujours 3 rotations séquentielles.

Maintenant, pour combiner les rotations autour de 3 axes, vous devez obtenir un seul axe et des angles simples qui représentent la rotation autour de X, Y, Z. En d'autres termes, vous devez utiliser une représentation Axe / Angle ou quaternion pour vous débarrasser des rotations séquentielles.

Cela se fait généralement en commençant par une orientation initiale (l'orientation peut être considérée comme un angle d'axe), généralement représentée comme un quaternion ou un angle d'axe, puis en modifiant cette orientation pour représenter l'orientation de votre destination. Par exemple, vous commencez par le quaterion d'identité, puis vous tournez par la différence pour atteindre l'orientation de destination. De cette façon, vous ne perdez aucun degré de liberté.

concept3d
la source
Marqué comme réponse car il semble perspicace.
Syntac_
J'ai du mal à comprendre ce que vous essayez de dire avec cette réponse. Est-ce simplement "ne stocke pas l'orientation d'un objet sous des angles d'Euler"? Et si oui, pourquoi ne pas simplement le dire?
Ilmari Karonen
@IlmariKaronen Cela pourrait être plus clairement énoncé, mais je pense que concept3d favorise la représentation axe-angle; voir la section 1.2.2 de ce document pour la relation entre l'angle-angle et les quaternions. La représentation axe-angle est plus facile à mettre en œuvre pour les raisons ci-dessus, elle ne souffre pas de blocage de cardan et (pour moi au moins) elle est tout aussi facile à comprendre que les angles d'Euler.
NauticalMile
@ concept3d, c'est très intéressant, et j'aime vraiment votre réponse. Il me manque cependant une chose, les gens interagissent avec l'ordinateur à l'aide d'un clavier et d'une souris, si nous pensons à la souris, nous parlons alors de deltas de souris x et y. Comment représenter ces deltas x, y avec un seul quaternion que nous pouvons utiliser pour générer la matrice de rotation, par exemple pour changer l'orientation d'un objet?
gmagno
@gmagno l'approche consiste généralement à projeter le mouvement de la souris sur les objets ou la scène et à calculer les deltas dans cet espace, vous le faites en lançant un rayon et calculez l'intersection. Recherche de lancer de rayon, projet et non-projet, je suis rude sur les détails car je ne travaillais pas en CG depuis des années. J'espère que cela pourra aider.
concept3d
2

Changer une combinaison de rotations de l'espace objet vers l'espace monde est trivial: il suffit d'inverser l'ordre dans lequel les rotations sont appliquées.

Dans votre cas, au lieu de multiplier les matrices Z × X × Y, il vous suffit de calculer Y × X × Z.

Une justification à cela peut être trouvée sur Wikipedia: conversion entre les rotations intrinsèques et extrinsèques .

sam hocevar
la source
Si cela était vrai, la déclaration suivante de votre source ne serait pas vraie, car les rotations seraient différentes: "Toute rotation extrinsèque équivaut à une rotation intrinsèque par les mêmes angles mais avec un ordre inversé de rotations élémentaires, et vice-versa . "
Syntac_
1
Je ne vois aucune contradiction ici. Ma réponse et cette affirmation sont toutes deux vraies. Et oui, effectuer des rotations dans l'espace objet et dans l'espace mondial donne des rotations différentes; c'est précisément le point, n'est-ce pas?
sam hocevar
Cette déclaration indique que la modification de l'ordre entraînera toujours la même rotation. Si un ordre produit une mauvaise rotation, l'autre ordre le sera également, ce qui signifie que ce n'est pas une solution.
Syntac_
1
Vous vous trompez. La modification de l'ordre n'entraîne pas la même rotation. Changer l'ordre et passer des rotations intrinsèques aux rotations extrinsèques entraîne la même rotation.
sam hocevar
1
Je ne pense pas comprendre votre question. Votre GIF montre une rotation d'environ 50 degrés autour Z(espace objet), puis 50 degrés autour X(espace objet), puis 45 degrés autour Y(espace objet). C'est exactement la même chose qu'une rotation de 45 degrés autour Y( espace mondial ), puis 50 degrés autour X( espace mondial ), puis 50 degrés autour Z( espace mondial ).
sam hocevar
1

Je vais fournir ma solution comme réponse jusqu'à ce que quelqu'un puisse expliquer pourquoi cela fonctionne.

À chaque rendu, je reconstruisais mon quaternion en utilisant les angles stockés dans mon vecteur de rotation, puis j'appliquais le quaternion à ma transformation finale.

Cependant, afin de le garder autour des axes mondiaux, j'ai dû conserver le quaternion sur toutes les images et ne faire pivoter les objets qu'en utilisant une différence d'angle, c'est-à-dire ..

// To rotate an angle around X - note this is an additional rotation.
// If currently rotated 90, apply this function with angle of 90, total rotation = 180.
D3DXQUATERNION q;
D3DXQuaternionRotation(&q, D3DXVECTOR3(1.0f, 0.0f, 0.0f), fAngle);
m_qRotation *= q; 

//...

// When rendering rebuild world matrix
D3DXMATRIX mTemp;
D3DXMatrixIdentity(&m_mWorld);

// Scale
D3DXMatrixScaling(&mTemp, m_vScale.x, m_vScale.y, m_vScale.z);
m_mWorld *= mTemp;

// Rotate
D3DXMatrixRotationQuaternion(&mTemp, m_qRotation);
m_mWorld *= mTemp;

// Translation
D3DXMatrixTranslation(&mTemp, m_vPosition.x, m_vPosition.y, m_vPosition.z);
m_mWorld *= mTemp;

(Verbose pour la lisibilité)

Je pense que dsilva.vinicius essayait d'arriver à ce point.

Syntac_
la source
1

Vous devrez enregistrer l'ordre des rotations.

Rotating around x 90 then rotate around z 90 !=
Rotating around z 90 then rotate around x 90.

Stockez votre matrice de rotation actuelle et prémultipliez chaque rotation au fur et à mesure.

Charles Beattie
la source
0

En plus de @ concept3d answer, vous pouvez utiliser 3 matrices de rotation extrinsèque pour faire pivoter autour de l'axe en coordonnées universelles. Citation de Wikipedia :

Les rotations extrinsèques sont des rotations élémentaires qui se produisent autour des axes du système de coordonnées fixes xyz. Le système XYZ tourne, tandis que xyz est fixe. En commençant par XYZ chevauchant xyz, une composition de trois rotations extrinsèques peut être utilisée pour atteindre n'importe quelle orientation cible pour XYZ. Les angles d'Euler ou de Tait Bryan (α, β, γ) sont les amplitudes de ces rotations élémentaires. Par exemple, l'orientation cible peut être atteinte comme suit:

Le système XYZ tourne autour de l'axe z de α. L'axe X est maintenant à l'angle α par rapport à l'axe X.

Le système XYZ tourne à nouveau autour de l'axe des x de β. L'axe Z est maintenant à l'angle β par rapport à l'axe z.

Le système XYZ tourne une troisième fois autour de l'axe z de γ.

Les matrices de rotation peuvent être utilisées pour représenter une séquence de rotations extrinsèques. Par exemple,

R = Z (γ) Y (β) X (α)

représente une composition de rotations extrinsèques autour des axes xyz, si elle est utilisée pour pré-multiplier les vecteurs de colonnes, tandis que

R = X (α) Y (β) Z (γ)

représente exactement la même composition lorsqu'il est utilisé pour post-multiplier les vecteurs de ligne.

Il vous faut donc inverser l'ordre des rotations par rapport à ce que vous feriez en utilisant des rotations intrinsèques (ou locales). @Syntac a demandé une rotation zxy, nous devrions donc faire une rotation extrinsèque yxz pour obtenir le même résultat. Le code est ci-dessous:

Explication des valeurs de matrice ici .

// Init things.
D3DXMATRIX *rotationMatrixX = new D3DXMATRIX();
D3DXMATRIX *rotationMatrixY = new D3DXMATRIX();
D3DXMATRIX *rotationMatrixZ = new D3DXMATRIX();
D3DXMATRIX *resultRotationMatrix0 = new D3DXMATRIX();
D3DXMATRIX *resultRotationMatrix1 = new D3DXMATRIX();

D3DXMatrixRotationX(rotationMatrixX, angleX);
D3DXMatrixRotationY(rotationMatrixY, angleY);
D3DXMatrixRotationZ(rotationMatrixZ, angleZ);

// yx extrinsic rotation matrix
D3DXMatrixMultiply(resultRotationMatrix0, rotationMatrixY, rotationMatrixX);
// yxz extrinsic rotation matrix
D3DXMatrixMultiply(resultRotationMatrix1, resultRotationMatrix0, rotationMatrixZ);

D3DXVECTOR4* originalVector = // Original value to be transformed;
D3DXVECTOR4* transformedVector = new D3DXVECTOR4();

// Applying matrix to the vector.
D3DXVec4Transform(transformedVector, originalVector, resultRotationMatrix1);

// Don't forget to clean memory!

Ce code est didactique, pas optimal, car vous pouvez réutiliser plusieurs matrices D3DXMATRIX.

dsilva.vinicius
la source
1
désolé, ce n'est pas correct. la multiplication matrice / vecteur est associative. c'est exactement la même chose que la multiplication matricielle combinée.
concept3d
Tu as raison. J'ai mélangé les concepts de rotations extrinsèques et intrinsèques.
dsilva.vinicius
Je vais corriger cette réponse.
dsilva.vinicius
La réponse est maintenant corrigée.
dsilva.vinicius