+ Répondre à la discussion
Affichage des résultats 1 à 7 sur 7
  1. #1
    Invité régulier
    Homme Profil pro wladimir perrad
    Inscrit en
    octobre 2011
    Messages
    17
    Détails du profil
    Informations personnelles :
    Nom : Homme wladimir perrad
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : octobre 2011
    Messages : 17
    Points : 9
    Points
    9

    Par défaut Problème de conversion de matrice en quaternion

    Bonjour, je développe un moteur 3D depuis quelques temps et j'ai plusieurs fois été confronté à des problème de conversion matrice->quaternion (j'utilise les quaternions pour faire des interpolations sphériques linéaires). J'avais contourné le problème en passant tout en quaternion, ce qui m'évitait de devoir faire ce genre de conversion, mais il reste encore un endroit dans le moteur où je suis obligé de faire appel à cette conversion (dans l'exporteur 3dsmax plus exactement).

    Donc j'ai une boite simple qui doit faire une rotation de 90 degrés et quand l'angle de rotation est légèrement inférieur à 90, ça ne pose pas de problème. Quand l'angle devient très proche de 90 degré ou légèrement supérieur, le quaternion calculé change de "signe"( il passe de (0.7, -0.7, 0, 0) à (-0.7, 0.7, 0, 0) ).

    Voici le code de test que j'ai fais et qui reproduit l'erreur :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    	CQuaternion q, q2;
    	CMatrix m( -2.3841858e-007f,	-1.0000000f,	5.8091246e-008f,	0.00000000f,
    			-0.99999934f,	1.1920929e-007f,   -0.0011870498f,	-1.0773603e-007f,
    	               0.0011870499f,	-5.8265869e-008f,	-0.99999940f,	2.4647131f,
    		             0.f,		0.f,		   0.f,		1.f );
     
    	m.GetQuaternion( q );
     
    	CMatrix m2( -1.1920929e-007f,	      -0.99999994f,	                 5.9604645e-008f,	0.00000000f	, 
    			-0.99930769f,		2.3841858e-007f,		0.037203975f,	    -1.0773603e-007f,
    			-0.037203975f,		-5.4016709e-008f,		-0.99930763f,	    2.4647131f,	
    			0.f,				0.f,		           0.f,	              1.f  );
     
    	m2.GetQuaternion( q2 );
    et voici la fonction de conversion de matrice en quaternion :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
     
    void CMatrix::GetQuaternion( CQuaternion& quat ) const
    {
    	float fTrace = m_00 + m_11 + m_22 + 1;
    	if( fTrace > 3.9999 )
    		quat.Fill( 0, 0, 0, 1 );
    	else
    	{
    		if ( fTrace > 0.0001 )
    		{
    			float S = sqrt( fTrace ) * 2;
    			quat.Fill( ( m_21 - m_12 ) / S, ( m_02 - m_20 ) / S, ( m_10 - m_01 ) / S, 0.25f * S );
    		}
    		else 
    		{
    			if( ( abs( m_00 ) > abs( m_11 ) ) && ( abs( m_00 ) > abs( m_22 ) ) )
    			{
    				if ( ( 1.0 + m_00 - m_11 - m_22 ) <= 0 )
    					throw 1;
    				float S = sqrt( 1.0 + m_00 - m_11 - m_22 ) * 2; // S=4*qx 
    				quat.Fill( 0.25 * S, ( m_01 + m_10 ) / S, (m_02 + m_20 ) / S, ( m_21 - m_12 ) / S );
    			} 
    			else if ( abs( m_11 ) > abs( m_22 ) ) 
    			{ 
    				if ( ( 1.f + m_11 - m_00 - m_22 ) <= 0.f )
    					throw 1;
    				float S = sqrt( 1.f + m_11 - m_00 - m_22 ) * 2; // S=4*qy
    				quat.Fill( ( m_01 + m_10 ) / S, 0.25f * S, ( m_12 + m_21 ) / S, ( m_02 - m_20 ) / S );
    			}
    			else
    			{
    				float S;
    				if ( ( 1.f + m_22 - m_00 - m_11 ) <= 0.f )
    				{
    					throw 1;
    				}
    				else
    				{
    					S = sqrt( 1.f + m_22 - m_00 - m_11 ) * 2.f; // S=4*qz
    					quat.Fill( ( m_02 + m_20 ) / S, ( m_12 + m_21 ) / S, 0.25f * S, ( m_10 - m_01 ) / S );
    				}
    			}
    		}
    	}
    }
    Voici les valeurs obtenues :
    q = ( 0.70182616, -0.70182621, 0.00042286396, 0.00038762530 )
    q2 = ( -0.70689392, 0.70689404, 0.013153042, 0.013157572 )

    Ici, on voit que pour des valeurs de matrice très proches (elles représentent des positions extrêmement proches sous 3dsmax), les quaternions obtenus sont totalement différents, ce qui occasionne des problèmes lors de l'interpolation.

    La position initiale et finale de mon objet sont bonnes, mais dans le 2eme cas, le changement de signe fait que l'objet tourne dans le sens contraire pour arriver à la position finale (en gros, au lieu d'aller de midi à 3h dans le sens des aiguilles d'une montre, il va de midi à 3h dans le sens contraire).

    Donc je me dis qu'il y a 2 possibilités : soit la conversion de matrice en quaternion n'est pas correcte, soit c'est l'interpolation qui s’emmêle les pinceaux à cause du changement de signe (en interprétant ça comme une rotation dans l'autre sens...).

    Pour info, voici le code pour l'interpolation de deux quaternions :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    void CQuaternion::Slerp( const CQuaternion& q0, const CQuaternion& q1, float t, CQuaternion& qr )
    {
    	float fInitAngle = q0.GetAngle();
    	float fFinalAngle = q1.GetAngle();
    	float Omega = ( fFinalAngle - fInitAngle ) / 2.f;
    	qr = ( q0 * sin( ( 1 - t ) * Omega ) + q1 * sin( t * Omega ) ) / sin( Omega );
    	CVector::Lerp( q0.m_vPosition, q1.m_vPosition, t, qr.m_vPosition );
    }
    ... ainsi que la conversion quaternion->matrice que j'utilise à la fin avant d'envoyer tout ça à opengl :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    void CQuaternion::GetMatrix( CMatrix& mat )
    {
    	mat.m_00 = 1 - 2 * m_y * m_y - 2 * m_z * m_z;
    	mat.m_01 = 2 * m_x * m_y - 2 * m_z * m_w;
    	mat.m_02 = 2 * m_x * m_z + 2 * m_y * m_w;
    	mat.m_03 = m_vPosition.m_x;
     
    	mat.m_10 = 2 * m_x * m_y + 2 * m_z * m_w;
    	mat.m_11 = 1 - 2 * m_x * m_x - 2 * m_z * m_z;
    	mat.m_12 = 2 * m_y * m_z - 2 * m_x * m_w;
    	mat.m_13 = m_vPosition.m_y;
     
    	mat.m_20 = 2 * m_x * m_z - 2 * m_y * m_w;
    	mat.m_21 = 2 * m_y * m_z + 2 * m_x * m_w;
    	mat.m_22 = 1 - 2 * m_x * m_x - 2 * m_y * m_y;
    	mat.m_23 = m_vPosition.m_z;
     
    	mat.m_30 = 0;
    	mat.m_31 = 0;
    	mat.m_32 = 0;
    	mat.m_33 = 1;
    }
    Bref, ça fait depuis des jours que je suis sur ce problème et je bloque. Merci d'avance pour votre aide

    EDIT : Si jamais c'est un problème de conversion matrice->quaternion, ça pourrait se résoudre d'une autre manière, en récupérant par exemple directement le quaternion d'un bone à partir de 3dsmax.

  2. #2
    Invité régulier
    Homme Profil pro wladimir perrad
    Inscrit en
    octobre 2011
    Messages
    17
    Détails du profil
    Informations personnelles :
    Nom : Homme wladimir perrad
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : octobre 2011
    Messages : 17
    Points : 9
    Points
    9

    Par défaut

    De plus en plus bizarre, je viens de vérifier, il existe effectivement une classe de quaternion sous 3dsmax et quand je l'utilise pour convertir la matrice renvoyée par max en quaternion, il me renvoie le même... De plus, j'ai fais d'autres tests suite à celui que j'ai posté plus haut et quand je re-converti mon quaternion "foireux" il me donne pourtant bien la bonne matrice.... J'en arrive à la conclusion que le problème ne vint pas de la conversion matrice->quaternion. et que les quaternions (0.7, -0.7, 0, 0) et (-0.7, 0.7, 0, 0) doivent probablement être équivalents.

    Pourtant, le résultat de l'interpolation entre q0 (mon quaternion d'origine) et q1 (le quaternion foireux) ne fonctionne pas correctement. J'en déduis que le problème vient peut-être de mon interpolation qui doit être mauvaise pour certains cas particuliers, ce qui est bizarre étant donné que je me suis contenté de reprendre la formule donnée sur ce site.

    Personne n'a d'idée pour me dépanner ?

  3. #3
    Expert Confirmé Sénior

    Profil pro Jean-Michel BORLOT
    Fabricant et casseur d'avions
    Inscrit en
    avril 2004
    Messages
    3 440
    Détails du profil
    Informations personnelles :
    Nom : Jean-Michel BORLOT
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Fabricant et casseur d'avions
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : avril 2004
    Messages : 3 440
    Points : 6 092
    Points
    6 092

    Par défaut

    Salut,

    Citation Envoyé par wperrad Voir le message
    J'en arrive à la conclusion que le problème ne vint pas de la conversion matrice->quaternion. et que les quaternions (0.7, -0.7, 0, 0) et (-0.7, 0.7, 0, 0) doivent probablement être équivalents.
    Et oui... par contre c'est une rotation de 180° et pas de 90° comme tu écris dans ton premier post...


    Citation Envoyé par wperrad Voir le message
    Personne n'a d'idée pour me dépanner ?
    Tu peux essayer d'inverser les composantes de ton quaternion cible, et faire l'interpolation. Ca devrait être plus mieux comme interpolation.

    Le problème vient de la représentation physique du quaternion. Il y a deux manière de représenter une rotation autour d'un axe, soit par la rotation d'angle alpha autour de du vecteur (x,y,z), soit par la rotation d'angle -alpha et de vecteur (-x,-y,-z). Si tu veux que l'interpolation ne soit pas trop brutale, il faut que les vecteurs de départ et d'arrivée ne soit pas trop différents. Or avec tes deux quaternions, ils sont opposés, d'où le "grand tour" fait pendant l'interpolation. D'où l'idée d'inverser le second quaternion (ce qui au niveau matrice ne modifie rien), afin de le "rapprocher" du premier.

    Au niveau du code, tu peux rajouter une condition qui calcule l'angle entre les vecteurs porteurs des deux quaternions, et si il est supérieur à 90° tu inverses le quaternion cible.
    "Errare humanum est, sed perseverare diabolicum"

    Ma page sur DVP.com

  4. #4
    Invité régulier
    Homme Profil pro wladimir perrad
    Inscrit en
    octobre 2011
    Messages
    17
    Détails du profil
    Informations personnelles :
    Nom : Homme wladimir perrad
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : octobre 2011
    Messages : 17
    Points : 9
    Points
    9

    Par défaut

    Citation Envoyé par plegat Voir le message
    Et oui... par contre c'est une rotation de 180° et pas de 90° comme tu écris dans ton premier post...
    En fait je ne parlais pas du passage de q2=(0.7, -0.7, 0, 0) à q2'(-0.7, 0.7, 0, 0) mais de q à q2 ou q2' (q étant la position initiale, q2 et q2' étant les positions d'arrivée de 2 animations différentes, une bonne et l'autre foireuse). Mais bref, ce n'est pas important pour la suite.


    Citation Envoyé par plegat Voir le message
    Tu peux essayer d'inverser les composantes de ton quaternion cible, et faire l'interpolation. Ca devrait être plus mieux comme interpolation.

    Le problème vient de la représentation physique du quaternion. Il y a deux manière de représenter une rotation autour d'un axe, soit par la rotation d'angle alpha autour de du vecteur (x,y,z), soit par la rotation d'angle -alpha et de vecteur (-x,-y,-z). Si tu veux que l'interpolation ne soit pas trop brutale, il faut que les vecteurs de départ et d'arrivée ne soit pas trop différents. Or avec tes deux quaternions, ils sont opposés, d'où le "grand tour" fait pendant l'interpolation. D'où l'idée d'inverser le second quaternion (ce qui au niveau matrice ne modifie rien), afin de le "rapprocher" du premier.

    Au niveau du code, tu peux rajouter une condition qui calcule l'angle entre les vecteurs porteurs des deux quaternions, et si il est supérieur à 90° tu inverses le quaternion cible.
    Ton idée est intéressante, je vais tester ça tout de suite. Merci !

  5. #5
    Expert Confirmé Sénior

    Profil pro Jean-Michel BORLOT
    Fabricant et casseur d'avions
    Inscrit en
    avril 2004
    Messages
    3 440
    Détails du profil
    Informations personnelles :
    Nom : Jean-Michel BORLOT
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Fabricant et casseur d'avions
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : avril 2004
    Messages : 3 440
    Points : 6 092
    Points
    6 092

    Par défaut

    Citation Envoyé par wperrad Voir le message
    q2 et q2' étant les positions d'arrivée de 2 animations différentes, une bonne et l'autre foireuse
    Euh... un quaternion ne donne pas une position... il représente une transformation. Et ici q2 et q2' représente la même transformation (à la petite différence près de quelques degrés), donc j'ai toujours un peu de mal à comprendre le côté "foireux"... D'ailleurs tu écris toi-même:

    Citation Envoyé par wperrad Voir le message
    La position initiale et finale de mon objet sont bonnes
    Donc rien de "foireux", c'est juste l'interpolation qui doit être adaptée...
    "Errare humanum est, sed perseverare diabolicum"

    Ma page sur DVP.com

  6. #6
    Invité régulier
    Homme Profil pro wladimir perrad
    Inscrit en
    octobre 2011
    Messages
    17
    Détails du profil
    Informations personnelles :
    Nom : Homme wladimir perrad
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : octobre 2011
    Messages : 17
    Points : 9
    Points
    9

    Par défaut

    Citation Envoyé par plegat Voir le message
    Euh... un quaternion ne donne pas une position... il représente une transformation. Et ici q2 et q2' représente la même transformation (à la petite différence près de quelques degrés), donc j'ai toujours un peu de mal à comprendre le côté "foireux"...
    Heu oui j'ai fais un lapsus, en fait j'ai 2 matrices m1 et m2 qui représentent chacune une orientation différente de mon bone dans l'espace (m1 étant l'orientation d'origine et m2 l'orientation d'arrivée) et je voudrais faire une interpolation entre ces deux orientations en fonction d'un paramètre t. Je converti donc chacune de mes 2 matrices en quaternion et je fais un slerp entre ces 2 quaternions. Une fois le quaternion intermédiaire obtenu, je le reconverti en matrice pour connaitre l'orientation intermédiaire de mon bone.


    Citation Envoyé par plegat Voir le message
    Donc rien de "foireux", c'est juste l'interpolation qui doit être adaptée...
    Je viens de regarder la FAQ pour les jeux et je vois que la méthode employée pour obtenir la matrice interpolée MI entre deux matrices est apparemment différente de la mienne : calcul d'une matrice de rotation R à l'aide de la matrice de départ Ms et d'arrivée Mf (avec R=inverse(Ms) * Mf), conversion de R en quaternion qr pour récupérer son angle et son axe de rotation, interpolation de l'angle en fonction de t pour récupérer le quaternion interpolé qi et conversion de ce quaternion en matrice MI. Je vais tester ça de suite et je te tiens au courant. Merci encore !

  7. #7
    Invité régulier
    Homme Profil pro wladimir perrad
    Inscrit en
    octobre 2011
    Messages
    17
    Détails du profil
    Informations personnelles :
    Nom : Homme wladimir perrad
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : octobre 2011
    Messages : 17
    Points : 9
    Points
    9

    Par défaut

    Ca maaaaaaaaaaaaarche !

    Bon là je n'ai testé que l'animation qui me posait problème, je vais faire d'autres tests plus poussés et je mettrais le sujet en résolu si c'est toujours bon.
    Encore merci !

+ Répondre à la discussion
Cette discussion est résolue.

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •