Les MipMaps

Les mipmaps permettent une amélioration flagrante de la qualité de rendu des textures en fonction de leur distance par rapport à la position de la caméra qui filme la scène. Prenons la place d’un programme de jeu qui veut afficher un mur. Ce dernier utilisera un cube avec une texture de mur. Etant très maniaque ce programmeur va utiliser une texture de grande qualité afin d’obtenir un mur réaliste. Certes pour une vue de près l’effet voulu sera réussit, mais imaginez le gâchis pour une vue de loin : non seulement Direct3D va tenter d’afficher la forme avec une texture importante, en réduisant sa taille arbitrairement le tout pour un rendu moyen. Quelle perte de performance et de qualité !


Il aurait mieux valu, lorsque la caméra s’éloigne faire en sorte que une texture deux fois moins grande soit utilisée. Moins grande certes mais réalisée par le programmeur ou selon les désires du programmeur !


 


Pourquoi créer ses propres textures alors les filtres pouvaient aisément réduire une image. Tout simplement parce que les filtres ne sont pas intelligents : ils réalisent leur tâche bêtement sans se soucier du contenu de la texture ni de l’information qu’elle véhicule. Faites ce test : ouvrez votre logiciel de dessin, créez une image avec un texte et réduisez le de 50% avec un filtre bilinéaire.


Vous obtenez quelque chose de la sorte :


 


 
Figure 1 — En bas 50% en bilinéaire


 


L’altération de l’information est flagrante ! Aucun filtre ne peut avoir une vue globale de la texture dans la mesure où ils travaillent au niveau des pixels. Dans le même sens aucun filtre ne peut quantifier l’information véhiculée par la texture et tenter de la sauvegarder au détriment des informations moins importantes.


L’homme, lui, le peut … A lui de créer les différentes textures correspondant à cette perte d’information. Il doit déterminer à partir de quel point le message de la texture devient illisible et créer une nouvelle texture adaptée qui continue à donner le message dans une nouvelle échelle. Au processus de mipmapping de substituer la texture originelle par la nouvelle texture plus petite quand cela devient nécessaire.


 



Figure 2 — La texture réduite à la main est plus lisible.


  


1 Présentation


 


L’ensemble des textures successivement réduites forme un Mipmap. MIP correspond à « Multi in parvo » en latin, c’est-à-dire « de nombreuse chose au même endroit ». Chaque texture de cet ensemble est un niveau de MIPmap ou LOD (Level Of Detail). Chaque niveau doit avoir pour hauteur et largeur la moitié de la hauteur et largeur du niveau précédent. Le dernier niveau a bien évidemment au moins un coté égal à 1. Sachant que la taille conseillée pour une texture est de 256 pixels il y’a donc au maximum 9 LOD (256 puis 128 puis 64, 32, 16, 8, 4, 2 et 1).


 Le mipmapping est très utile dans la mesure ou il permet de préserver l’information dans le sens voulu par le développeur mais aussi en évitant à la carte graphique l’affichage de grosses textures lorsque ce n’est pas nécessaire.


Filtre trilinéaire et mipmapping


Les transitions entre les différents LOD sont souvent visibles lorsqu’on regarde au loin. Le processus de mipmapping est heureusement capable de filtrer deux niveaux de mipmap consécutif pour en calculer la moyenne de couleur. La transition devient pratiquement invisible. Il s’agit là du filtre bilinéaire, mais, dans la mesure où il porte sur une transition entre deux texture on parle de filtrage trilinéaire.


LOD bias[1]


Il arrive que pour la beauté du jeu il faille garder un LOD plus longtemps que prévu en retardant le passage à un LOD inférieur lorsque la caméra s’éloigne. C’est le LOD bias qui permet de réaliser cette opération. Si une surface s’éloigne de l’utilisateur, augmenter le LOD bias revient à retarder l’utilisation du LOD inférieur. Au contraire, en diminuant sa valeur, on passe plus rapidement aux LOD inférieurs.


 


2. Notre Sample


 


Pour mettre en évidence tout ce que nous venons d’apprendre, rien de tel qu’un bon exemple en 3D. Tout comme le code précédent, nous allons faire en sorte de pouvoir modifier toutes les propriétés liées au mipmapping par l’intermédiaire des touches F1 à F8. Un mipmap est un ensemble de texture dont la taille décroît. Il est nécessaire de créer cet ensemble. Pour cela les textures suivantes seront utilisées :


 


Figure 3—Textures de couleurs…


 


Il s’agit d’une série d’image en couleur dont la taille décroît. Les couleurs permettront d’aisément discerner les différents LOD lors des modifications effectuées par l’utilisateur dans le programme. De plus pour avoir une idée plus précise du rendu à l’écran, un autre mipmap sera utilisé, cette fois ci pas de couleur, mais un motif : un sol pavé.


 



Figure 4— …Textures de sol.


 


Pour l’affichage, un plan fixe formé de 25*25 cases sera dessiné à l’écran. De même , un petit moteur de déplacement à la manière des Quake like sera programmé dans le but de pouvoir déplacer la caméra et admirer les conséquences sur les modifications de LOD.


 


Prêt ? C’est parti !


 


3. Etude du code source 


 


 


Le fichier principal commence par la déclaration de deux structures : une structure Vertex définissant les propriétés des vertex utilisés dans le programme, et une énumération nommée Filtres qui nous permettra de donner un nom aux filtres voulus par l’utilisateur dans le programme.


 


Vient ensuite la déclaration de 4 Textures :


 


 


/// <summary>


/// Texture pour le mipmapping


/// </summary>


Texture textureMipMappingCouleur;


    


/// <summary>


/// Texture pour le mipmapping


/// </summary>


Texture textureMipMappingSolContour;


    


/// <summary>


/// Texture pour le mipmapping


/// </summary>


Texture textureMipMappingSolPur;


    


/// <summary>


/// Texture pour le mipmapping actuellement utilisée


/// </summary>


Texture texture;


 


 


Ces textures sont en fait des mipmap. Elles posséderont différents LOD. En fait une texture simple est un mipmap avec un seul LOD : le niveau 0. Nous aurons un mipmap pour l’étude sur les textures colorisées, un mipmap pour l’étude sur des textures de sol avec contour, et encore un pour les textures de sol sans contour. La dernière texture est en fait le mipmap actuellement affiché par le programme. Il pointera sur l’un des trois mipmap précédemment énumérés.


Terminons les déclarations par différentes variables de sauvegarde des paramétrages de l’utilisateur dans sa découverte du mipmapping :


 


 


/// <summary>


/// Filtres de base, on commence en linéaire


/// </summary>


Filtres           magFilterType  = Filtres.Linear;


Filtres           minFilterType  = Filtres.Linear;


Filtres           mipFilterType  = Filtres.Linear;


 


/// <summary>


/// Niveau d’anisotropy


/// </summary>


int   anisotropy    = 1;


 


/// <summary>


/// Level de détail à utiliser


/// </summary>


float mipMapLodBias = 0.0f;


 


/// <summary>


/// Inidique qu’une mise à jour doit être faite


/// </summary>


bool mettreAJour = true;


 


 


Trois variables de type Filtre pour les filtres utilisés avec la magnification, minification et le mipmap. Le niveau de l’anisotropie, la valeur du LOD bias. Un booléen enfin pour déterminer si l’utilisateur a modifié quelque chose.


 


Ce sont ChargementTexturesEtMipMap() et ChargementTexturesMipMap() qui vont initialiser les mipmap.


La première se présente ainsi :


 


 


    /// <summary>


    /// Charge les mipmap couleur, sol contour, et sol pur


    /// </summary>


     public void ChargementTexturesEtMipMap()


     {


          this.textureMipMappingCouleur = CreateTexture( device, “base.bmp”, Format.A8R8G8B8);


          this.textureMipMappingSolContour = CreateTexture( device, “scbase.bmp”, Format.A8R8G8B8);


          this.textureMipMappingSolPur = CreateTexture( device, “sbase.bmp”, Format.A8R8G8B8);


 


          ChargementTexturesMipMap(textureMipMappingCouleur, “”);


          ChargementTexturesMipMap(textureMipMappingSolContour, “sc”);


          ChargementTexturesMipMap(textureMipMappingSolPur, “s”);


 


 


          this.texture = this.textureMipMappingCouleur;


     }


 


 


 


 


Elle créé les trios textures vue précédemment avec base.bmp, scbase.bmp et sbase.bmp. Chaque texture dans le programme possède le même nom mais avec un préfixe différent :


 


 


  • Pas de préfixe pour les textures du mipmap colorisé
  • « sc » pour le mipmap sol avec contour
  • « s » pour le sol pur.

 


 


Ces trois initialisations ne servent qu’à créer la texture car en fait leur contenu sera effacé par les différents LOD du mipmap.


 


Les trois instructions suivantes chargent le mipmap de chacun d’entre elles :


 


 


ChargementTexturesMipMap(textureMipMappingCouleur, “”);


ChargementTexturesMipMap(textureMipMappingSolContour, “sc”);


ChargementTexturesMipMap(textureMipMappingSolPur, “s”);


 


 


 


La méthode ChargementTextureMipMap créée dans notre programme accepte en paramètre, la texture a charger en mipmap et le préfixe des textures à charger.


 


La méthode se termine enfin en faisant pointer la propriété texture sur le mipmap qui sera affiché au démarrage.


 


 


Analysons ChargementTexturesMipMap :


 


 


     /// <summary>


     /// Chargement des textures mipmap


     /// </summary>


     public void ChargementTexturesMipMap(Texture mipmap, string préfixe)


     {


          Texture[] textures = new Texture[6];


 


 


          // Charge nos 6 textures


          textures[0] = CreateTexture( device, préfixe +”256.bmp”, Format.A8R8G8B8);


          textures[1] = CreateTexture( device, préfixe +”128.bmp”, Format.A8R8G8B8);


          textures[2] = CreateTexture( device, préfixe +”64.bmp”, Format.A8R8G8B8);


          textures[3] = CreateTexture( device, préfixe +”32.bmp”, Format.A8R8G8B8);


          textures[4] = CreateTexture( device, préfixe +”16.bmp”, Format.A8R8G8B8);


          textures[5] = CreateTexture( device, préfixe +”8.bmp”, Format.A8R8G8B8);


    


 


          //création des surfaces pour le copier/coller


          Surface destination = null;


          Surface source  = null;


 


          int i;


 


          for( i = 0; i < 6; ++i )


          {


                //pointer sur la surface texture du LOD i


                destination = texture.GetSurfaceLevel(i);


 


                //pointer vers la surface de la texture i


                source = textures[i].GetSurfaceLevel(0);


 


                //charger dans destination la source, sans filtre


                SurfaceLoader.FromSurface(destination, source, Filter.None, 0);


          }


 


          //Libérer les textures temporaires utilisées


          for( i = 0; i < 6; ++i )


                textures[i].Dispose();


     }


 


 


 


La méthode commence par charger dans un tableau de 6 textures les six LOD du mipmap. Elle va ensuite, charger à chaque LOD du mipmap passé à la méthode les 6 textures chargées :


 


 


          for( i = 0; i < 6; ++i )


          {


                //pointer sur la surface texture du LOD i


                destination = mipmap.GetSurfaceLevel(i);


 


                //pointer vers la surface de la texture i


                source = textures[i].GetSurfaceLevel(0);


 


                //charger dans destination la source, sans filtre


                SurfaceLoader.FromSurface(destination, source, Filter.None, 0);


          }


 


 


Pour les 6 itérations, on fait pointer destination sur le LOD du mipmap. On fait pointer source sur le LOD 0 de la texture de la case du tableau lu. Chaque texture de ce tableau n’est pas un mipmap et une seule texture, il n’y a donc qu’un LOD : celui du niveau 0. A l’aide de la méthode FromSurface de la classe utilitaire SurfaceLoader on place dans destination le contenu de source. Le tout sans filtrage et avec une clé de couleur de transparence noire (la valeur 0). En gros cette boucle chaque dans le mipmap a chacun des 6 LOD, les 6 textures chargées au départ.


 


Cette opération étant effectuée, on peut libérer la mémoire prise par le tableau.


 


 


La création de mipmap est une opération relativement facile (on perd plus de temps à créer les textures des LOD !). Pourquoi s’en passer quand on voit le gain de performances et de qualité visuelle ?


 


Remarque : Pour déterminer le nombre de level d’un mipmap utilisez la propriété LevelCount de cette manière :


           int nombreLOD = mipmap.LevelCount;


 


   


Trois méthodes, travaillent sur le sampler. Elles se présentent comme ceci :


 


 


private void SetMagnificationFilter()


{ 


     if( this.magFilterType == Filtres.None )


          this.device.SetSamplerState(0, SamplerStageStates.MagFilter, (int)TextureFilter.None);


    


     if( this.magFilterType == Filtres.Point )


          this.device.SetSamplerState(0, SamplerStageStates.MagFilter, (int)TextureFilter.Point);


 


     if( this.magFilterType == Filtres.Linear )


          this.device.SetSamplerState(0, SamplerStageStates.MagFilter, (int)TextureFilter.Linear);


 


     if( this.magFilterType == Filtres.Anisotropic )


          this.device.SetSamplerState(0, SamplerStageStates.MagFilter, (int)TextureFilter.Anisotropic);


}


 


 


 


Il existe aussi 2 autres méthodes similaires SetMinificationFilter et SetMipMapFilter qui travaillent de la même manière.


 


Les modifications sont effectuées par le groupe d’instructions :


 


 


this.SetMinificationFilter();


this.SetMagnificationFilter();


this.SetMipMapFilter();


device.SetSamplerState(0, SamplerStageStates.MaxAnisotropy, anisotropy );


device.SetSamplerState(0, SamplerStageStates.MipMapLevelOfDetailBias, mipMapLodBias );


 


this.Text = “Minification = ” + this.minFilterType + ” | Magnification = ” + this.magFilterType + ” | MipMap = ” + this.mipFilterType + ” | MaxAnisotropy = ” + anisotropy + “|LOD = ” + mipMapLodBias;


 


 


Les trois appels de methodes vues précédemment, suivit par deux modifications du sampler pour mettre à jour l’anisotropie et le LOD bias.


La dernière instruction mets à jour le titre de la fenêtre pour pouvoir connaître l’état des filtres.


  


  


4. Touches de l’exécutable


  


Page Haut/Bas : Déplacement en hauteur.


Flèches gauche/droite : Tourner la tête.


Flèches haut/bas : avancer/reculer.


+/- : Regarder en haut/en bas


Touche numérique 1/2/3 : Mipmap colorisé/sol avec contour/sol pur.


F1 : modification du filtre de minification.


F2 : modification du filtre de magnification.


F3 : modification du filtre de mipmapping.


F4/F5 : modification du LOD bias.


F6/F7 : modification de la valeur de l’anisotropie.


 


5. Etude de l’exécutable


  


Le code du programme étant compris voyons ce qu’il peut nous apporter. Lancez le, l’écran au démarrage devrait être le suivant :


 


 
Figure 5—On remarque bien les plans translatés et le mipmapping


 


En se déplaçant en avant en arrière on remarque les ondes des LOD suivre et s’avancez ou reculer. De même en s’approchant du sol, les LOD avec les index les plus petit (dont les plus détaillés) apparaissent (notamment la texture rouge et bleu).


 


En hauteur, la touche F1 qui modifie le filtre de minification agit. Le filtre point effectue ainsi une opération de mauvaise qualité. La minification agit car en hauteur les différents plans du sol ont une taille inférieure à la taille de la texture. Au sol si la minification ne fait rien, au contraire la magnification agit. Même remarque : le filtre point dégrade l’affichage (dommage, c’est le plus rapide).


 



Figure 6—Point et Lineaire … le choix est aisé.


 


Jouons maintenant avec le mipmapping (touche F3). Si nous prenons le filtre None (qui desactive le mipmapping) évidemment le sol devient rouge : il n’y a plus qu’une texture dans le mipmap. Avec le filtre point, on obtient un affichage certes hideux, mais très intéressant dans la mesure où on détermine bien les différents LOD. On peut passer en mode sol pur pour discerner au mieux l’effet dans un jeu.


 



Figure 7—Le mipmapping en mode point est … à éviter.


 


Avec un filtre Linéaire ou Anisotropique les différences de LOD s’estompent. Jouons maintenant pour terminer avec le LOD bias. Il permet de retarder ou s’accélérer le passage à un autre LOD.




Figure 8 —LOD bias = 0


 


Figure 9—LOD bias = 1 : on est plus rapidement à des LOD moins détaillés


 


Figure 10—LOD bias = -3 : le LOD détaillé en face reste affiché à une distance plus élevé de la caméra.


 


 


Ce petit programme d’exemple devrait encore avoir éclaircit nombre de point difficiles à assimiler (notamment les filtres). Quelqu’un qui maîtrise bien les fonctionnalités de cet utilitaire pourra tenter de discerner les nuances des filtres plus seulement avec le mipmap colorisé.


 


 


Télécharger le Sample 


 


telecharger Vous pouvez télécharger le sample ici.




[1] Bias peut être traduit par  correction ou affinage ici..

2 thoughts on “Les MipMaps”

  1. j’aimerais dire que cet articles est extrèmement bien realisé, and m’a été vraiment utile !

    J’espere qu’il pourra convaincre tout ses lecteurs, mais vu le soin que l’auteur a mis à cet article je me fait pas de soucis.

    La qualité est d’autant plus à souligner que le mip-mapping est un “must-have” des applications 3D.

    Et parceque les dessins valent mieux que de long discours, les screenshot viennent grandement faciliter la comprehension de l’article.

    juste un petit regret avec les smiley inclu dans le code-source, mais ca c’est pas de la faute de l’auteur mais du formulaire du site…

    Très Bien !

          Lucyberad

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>