Annexe : Transformations et Matrices

Retourner au sommaire des cours  


Les transformations dans l’espace représentent le traitement clé de voute dans toute application 3D. C’est par les transformations que les objets prennent vie. Une transformation pour parler simplement est une suite de multiplication de matrices. Généralement on distingue 3 types de matrices pour travailler sur un modèle 3D avant son affichage à l’écran : les matrices de rotation, les matrices de redimentionnement, les matrices de translation. Lorsqu’on veut déplacer un objet de le faire tourner il suffit donc de réaliser des traitements du type :


 matriceTransformation = matriceRotation * matriceTranslation


Ici nous réalisons une rotation puis une translation. L’objet représente le résultat de cette transformation et peut être vu comme un condensé unique des deux matrices. C’est cette matrice qui sera affectée à la matrice monde (ou World) pour placer l’objet à l’écran avant son affichage. 


Vous devez, lorsque vous effectuez ce genre de calculs, garder en tête que :


  • Toute transformation sur un objet s’effectué par rapport au repère de cet objet.
  • La multiplication de matrices, n’est pas commutative.

 Revenons justement sur ces deux notions.



 

Repère de l’objet 


 Lorsqu’on effectue une transformation sur un objet, cette transformation se fait par rapport au point d’origine dans le repère de l’objet. Le repère de l’objet est le repère par rapport auquel vous vous référez lorsque vous placez les vertices du modèle que vous voulez afficher. 


Prenons par exemple les huits points de ce cube :


vertices[0].Position = new Vector3(-10f, -10f, -10f);


vertices[1].Position = new Vector3(-10f, -10f, 10f);


vertices[2].Position = new Vector3(10f, -10f, 10f);


vertices[3].Position = new Vector3(10f, -10f, -10f);


vertices[4].Position = new Vector3(10f, 10f, 10f);


vertices[5].Position = new Vector3(10f, 10f, -10f);


vertices[6].Position = new Vector3(-10f, 10f, -10f);


vertices[7].Position = new Vector3(-10f, 10f, 10f);


Ici le cube fait 20 unités de coté (de -10 à +10).  Le point d’origine (0, 0, 0) du repère qui lui est associé se trouve pile au centre du cube. Si nous effectuons une rotation du cube, celle-ci s’effectuera donc par rapport à cette origine qui, étant au centre du cube, donnera l’impression que le cube tourne sur lui-même. L’image suivant illustre cela :


Le cube tourne par rapport au centre du repère associé qui se trouve au centre du cube


 (nous effectuons ici une rotation sur X et Y).


 Maintenant regardons un cube créé avec les 8 points suivants : 

vertices[0].Position = new Vector3(0f, 0f, 0f);vertices[1].Position = new Vector3(0f, 0f, 20f);vertices[2].Position = new Vector3(20f, 0f, 20f);vertices[3].Position = new Vector3(20f, 0f, 0f);vertices[4].Position = new Vector3(20f, 20f, 20f);vertices[5].Position = new Vector3(20f, 20f, 0f);vertices[6].Position = new Vector3(0f, 20f, 0f);//vertices[7].Position = new Vector3(0f, 20f, 20f);

Là encore, chaque coté du cube possède une taille de 20 unités. Mais le centre du repère (0 ,0 ,0) correspond au premier point. La rotation que nous avons effectué précédemment associée à ce cube se fera par rapport à ce point. Le cube ne tournera donc plus sur lui-même mais par rapport à un de ses sommets. L’image suivant illustre celà :


La rotation est excentrée parcequ'effectuée par rapport au centre du repère associée qui n'est pas au centre du cube


 Ici, c’est le point de couleur jaune qui est au centre du repère.


Nous venons de faire un grand pas pour bien comprendre comment maitriser les matrices et les transformations associées.


 


Ordre des transformations / Communativité

Au tout début de cet article, nous avons vu l’instruction suivante : 

  matriceTransformation = matriceRotation * matriceTranslation

Sachant que la classe Matrix nous offre un ensemble de méthodes statiques clé en main pour créer ce type de matrices, en langage C# nous aurions quelque chose du genre 

matriceTransformation = Matrix.CreateRotationY(MathHelper.PIOver2)* Matrix.CreateTranslation(20, 0, 0)

Pour créer une rotation de 90° suivie d’une translation. Le verbe au participe passé “suivie’ est important dans cette phrase. Je n’ai pas utilisé le complément “et”. En effet il y’a une notion d’ordre dans le multiplication des matrices ; C’est au niveau de ce genre de traitement qu’il nous faut faire attention. La multiplication de matrices n’est en effet pas commutative ; l’instruction précécent ne donne pas le même résultat que :

matriceTransformation = matriceTranslation *matriceRotation


On peut se référer au premier point de cet article où les transformations s’effectuent par rapport au modèle de l’objet. Prenons l’affichage suivant :


Notre cube au départ


Nous disposons ici d’un cube. Si nous effectuons une rotation sur l’axe Z, l’arrete située sur l’axe X se trouvera sur l’axe Y. Le pavé aura tourné autour de son origine. Si maintenant nous effectuons une translation sur l’axe X, le pavé tourné va être déplacé et nous obtiendrons comme résultat pour : 


  matriceTransformation = matriceRotation * matriceTranslation


 Le rendu :



Revenons maitenant au premier affichage et essayons l’instruction :


matriceTransformation = matriceTranslation *matriceRotation


La transformation commence d’abord ici par une translation d’une distance que nous appelerons D. Nous obtenons donc le même résultat que l’image précédente mais avec un pavé non tourné. Une rotation est ensuite effectée. C’est là que la différence se fait pleinement sentir : la rotation se situe non pas par rapport au pavé mais par rapport au centre du repère de l’objet qui se situe à une distance D de l’objet. La rotation va donc faire tourner l’objet autour d’un cercle imaginaire possèdant un rayon d’une taille équivalente à D. Le résultat donne donc :



Cet exemple illustre parfaitement la non commutativité de la multiplication de matrices. Même si cet exemple est simple, il faut toujour garder à l’esprit cette loi fondamentale, surtout lorsqu’on travaille sur un très grand nombre de matrices qui interagissent entre elles.


 


Exemple de classe “Transform-Safe”


Pour terminer cet article, voici une classe dont hérite la plupart des objets métier affiché à l’écran dans les applications 3D que je réalise. Elle donne à ses objets enfants un ensemble de propriétés et de méthodes simples qui permettent d’interragir avec l’objet de manière transparente et intuitive pour modifier l’objet avant son rendu.

/// <summary>/// <para>Defines a transformable object.</para>/// </summary>public class TransformBase{     #region Private members     [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]    private Matrix _translation;    [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]    private Matrix _rotation;    [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]    private Matrix _scale;    [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]    float _rotationX, _rotationY, _rotationZ;     #endregion      #region Properties     /// <summary>    /// <para>Gets the associated transformation matrix.</para>    /// </summary>    public Matrix Transform    {        get        {            return this._scale * this._rotation * this._translation;        }    }      /// <summary>    /// <para>Gets or sets the current object’s position.</para>    /// </summary>    public Vector3 Position    {        get        {            return new Vector3(this._translation.M41, this._translation.M42, this._translation.M43);        }        set        {            this._translation.M41 = value.X;            this._translation.M42 = value.Y;            this._translation.M43 = value.Z;        }    }     /// <summary>    /// <para>Gets or sets the position on the X axis.</para>    /// </summary>    public float X    {        get        {            return this._translation.M41;        }        set        {            this._translation.M41 = value;        }    }     /// <summary>    /// <para>Gets or sets the position on the Y axis.</para>    /// </summary>    public float Y    {        get        {            return this._translation.M42;        }        set        {            this._translation.M42 = value;        }    }     /// <summary>    /// <para>Gets or sets the position on the Z axis.</para>    /// </summary>    public float Z    {        get        {            return this._translation.M43;        }        set        {            this._translation.M43 = value;        }    }     /// <summary>    /// <para>Gets or sets the current rotation in radian on X axis.</para>    /// </summary>    public float XRotation    {        get        {            return this._rotationX;        }        set        {            this.Rotate(value, _rotationY, _rotationZ);        }    }     /// <summary>    /// <para>Gets or sets the current rotation in radian on Y axis.</para>    /// </summary>    public float YRotation    {        get        {            return this._rotationY;        }        set        {            this.Rotate(_rotationX, value, _rotationZ);        }    }     /// <summary>    /// <para>Gets or sets the current rotation in radian on Z axis.</para>    /// </summary>    public float ZRotation    {        get        {            return this._rotationZ;        }        set        {            this.Rotate(_rotationX, _rotationY, value);        }    }     /// <summary>    /// <para>Gets or sets the current scale on X axis.</para>    /// </summary>    public float XScale    {        get        {            return this._scale.M11;        }        set        {            this._scale.M11 = value;        }    }     /// <summary>    /// <para>Gets or sets the current scale on X axis.</para>    /// </summary>    public float YScale    {        get        {            return this._scale.M22;        }        set        {            this._scale.M22 = value;        }    }     /// <summary>    /// <para>Gets or sets the current scale on X axis.</para>    /// </summary>    public float ZScale    {        get        {            return this._scale.M33;        }        set        {            this._scale.M33 = value;        }    }     #endregion      #region Constructor     /// <summary>    /// <para>Default constructor.</para>    /// </summary>    public TransformBase()    {        Reset();    }     #endregion      #region Public methods     /// <summary>    /// <para>Reset the matrices to default position.</para>    /// </summary>    public void Reset()    {        this._translation = Matrix.Identity;        this._rotation = Matrix.Identity;        this._scale = Matrix.Identity;        this._rotationX = _rotationY = _rotationZ = 0.0f;    }     #endregion      #region Transform methods     #region Translate     /// <summary>    /// <para>Move current object to the specified location.</para>    /// </summary>    /// <param name=”x”>Position X-axis value.</param>    /// <param name=”y”>Position Y-axis value.</param>    /// <param name=”z”>Position Z-axis value.</param>    public void MoveTo(float x, float y, float z)    {        this._translation.M41 = x;        this._translation.M42 = y;        this._translation.M43 = z;    }     /// <summary>    /// <para>Move current object to the specified location.</para>    /// </summary>    /// <param name=”translation”>Position’s vector.</param>    public void MoveTo(Vector3 translation)    {        this.MoveTo(translation.X, translation.Y, translation.Z);    }     /// <summary>    /// <para>Translate current object from the current position with the specified vector.</para>    /// </summary>    /// <param name=”x”>Vector X-axis value.</param>    /// <param name=”y”>Vector Y-axis value.</param>    /// <param name=”z”>Vector Z-axis value.</param>    public void Translate(float x, float y, float z)    {        _translation.M41 += x;        _translation.M42 += y;        _translation.M43 += z;    }     /// <summary>    /// <para>Translate current object from the current position with the specified vector.</para>    /// </summary>    /// <param name=”translation”>Translation vector</param>    public void Translate(Vector3 translation)    {        this.Translate(translation.X, translation.Y, translation.Z);    }     #endregion      #region Rotate     /// <summary>    /// <para>Rotate current object on the X,Y,Z axis.</para>    /// </summary>    /// <param name=”x”>X axis rotation in radians</param>    /// <param name=”y”>Y axis rotation in radians</param>    /// <param name=”z”>Z axis rotation in radians</param>    public void Rotate(float x, float y, float z)    {        this._rotationX = x;        this._rotationY = y;        this._rotationZ = z;        this._rotation = Matrix.CreateRotationX(x)*Matrix.CreateRotationY(y)*Matrix.CreateRotationZ(z);    }     /// <summary>    /// <para>Make a rotation from the current rotation.</para>    /// </summary>    /// <param name=”x”>X axis rotation in radians</param>    /// <param name=”y”>Y axis rotation in radians</param>    /// <param name=”z”>Z axis rotation in radians</param>    public void Turn(float x, float y, float z)    {        this._rotationX += x;        this._rotationY += y;        this._rotationZ += z;        this.Rotate(this._rotationX, this._rotationY, this._rotationZ);    }      #endregion      #region Scale     /// <summary>Resize the current object.</summary>    /// <param name=”x”>Scale factor for X values.</param>    /// <param name=”y”>Scale factor for Y values</param>    /// <param name=”z”>Scale factor for Z values</param>    public void Resize(float x, float y, float z)    {        this._scale.M11 = x;        this._scale.M22 = y;        this._scale.M33 = z;    }     /// <summary>Scale current object from current size.</summary>    /// <param name=”x”>X</param>    /// <param name=”y”>Y</param>    /// <param name=”z”>Z</param>    public void Scale(float x, float y, float z)    {        this._scale.M11 += x;        this._scale.M22 += y;        this._scale.M33 += z;    }     #endregion     #endregion }

 


Vous remarquerez ici des instructions du type :

            this._scale.M11 = x;            this._scale.M22 = y;            this._scale.M33 = z; 

Les propriété comme M11 ou M23 correspondent à des valeurs situées dans la matrices. Nous reviendrons dans un prochain article sur leur raison d’être.


 


Conclusion 


Cet article est incontournable si vous ne maitrisez pas encore la notion de transformation. Ayez toujours en tête les principes élémentaires qu’il a énuméré afin de vous éviter de nombreuses heures de recherches après des invraissemblances graphiques, qui sont, au final, relativement simples à éviter.


[Soon]


Valentin Billotte


[Help] 


Retourner au sommaire des cours 

3 thoughts on “Annexe : Transformations et Matrices”

  1. Je crois qu’il faut bien un bon bouquin pour comprendre le principe de la représentation 3D et du calcul matricel :

    Voici celui que je conseille au lecteur de cet article, il est en anglais, mais super progressif, et permet de tout comprendre :

    http://www.amazon.com/Schaums-Outline-Computer-Graphics-Zhigang/dp/0071357815/sr=1-1/qid=1172031362/ref=sr_1_1/104-3748189-4519935?ie=UTF8&s=books

    http://catalogs.mhhe.com/mhhe/viewProductDetails.do?isbn=0071357815

    Il existe en version française mais ça date de 1986 il ne me semble plus réédité donc possible de le trouver en bibliotheque sous le titre “INFOGRAPHIE” et l’auteur est PLASTOCK.

    Sinon, les tutos de XNA sont exceptionels, j’y ai passé ma nuit :-)

    Je reviendrais lire la suite que j’attends avec impatience.. Un immense Merci à toi !

  2. Bonjour,

    Tout d’abord, merci pour tous ces tutos, je sens que je vais y passer pas mal de temps !

    Une petite question tout de même: l’exemple de classe “Transform-Safe” apparait illisible pour moi: il n’y a aucun retour à la ligne; j’ai donc un peu de mal à en tirer ce qui m’interresse…
    Est-ce qu’il y aurait un moyen de la voir en version indentée ? (Je précise que j’ai la même chose sous IE6 et sous Firefox…)

    Encore merci pour toutes ces infos et bon courage pour la suite !

  3. j’essayerais de te faire ca aujourd’hui ou demain

    c’est du à la nouvelle version de l’application faisant tourner ce blog :/

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>