FAQ Programmation 3D
FAQ Programmation 3DConsultez toutes les FAQ
Nombre d'auteurs : 7, nombre de questions : 67, dernière mise à jour : 14 juin 2021
- Pourquoi vaut-il mieux utiliser des textures dont les dimensions sont des puissances de 2 ?
- Dans quel ordre afficher mes polygones ?
- Quels sont les goulots d'étranglement possibles, et comment les déceler/corriger ?
- Comment effectuer des captures-écrans d'une résolution supérieure à celle de mon application ?
- Comment animer un modèle 3D ?
- Quel format de modèle 3D utiliser ?
- Comment avoir une texture « trouée » ?
- 2.2.1. Culling (élimination des faces) (5)
- 2.2.2. Techniques d'illumination (9)
Certains anciens hardwares graphiques ne supportent pas l'adressage de textures dont les dimensions sont différentes de puissances de 2. C'est-à-dire que 1024x512 sera supporté par cet ancien hardware, mais que 1024x768 ne le serait pas. Il faut bien comprendre que l'on parle de textures et non pas de surfaces, sachant que ces hardwares devraient être capables de créer un back buffer de dimension 1024x768 sans problème.
Ces hardwares sont probablement obsolètes aujourd'hui donc ce n'est pas la raison pour laquelle on préfère toujours les dimensions en puissance de 2.
Les deux autres raisons sont la performance et la compatibilité avec toutes les fonctionnalités. Certaines opérations comme la division et les modulo sont beaucoup plus rapides en puissance de 2, et quasiment gratuites quand elles sont précablées sous forme de transistors ; ce qui fait que beaucoup de hardwares graphiques ne supportent la totalité des opérations que sur ce genre de textures pour économiser des transistors. Certains hardwares sont capables de lire depuis une texture non puissance de 2, mais ne sont pas capables :
- d'utiliser un mode d'adressage autre que CLAMP ;
- d'utiliser du mipmapping sur cette texture ;
- d'utiliser un format compressé comme DXT1 / DXT5.
Certaines limitations s'appliquent également sur les shaders.
Par ailleurs sur les hardwares les plus répandus, lire depuis une texture puissance de 2 est beaucoup plus efficace grâce à un swizzling en mémoire (modification explicite de la position en mémoire physique pour améliorer la cohérence des accès mémoire). Cela reste vrai même sur les hardwares qui supportent les accès aux textures non puissance de 2 sans limitation (à l'heure actuelle seules les GeForce 6 et supérieures en sont capables).
Il y a donc un avantage compétitif en performance et en qualité d'imposer à vos artistes de n'utiliser que des textures puissance de 2 dans leurs modèles 3D. Pour les textures de rendu (rendertarget textures), il est par contre plus intéressant de choisir le mode qui minimise la quantité de mémoire utilisée (1280x960 est préférable à 2048x1024 en termes de consommation mémoire) d'autant plus que le swizzling est contreproductif dans ce cas--là.
Il convient de distinguer deux cas : les faces opaques, et les faces transparentes (celles faisant intervenir l'alpha-blending).
L'ordre d'affichage des faces opaques n'influera pas sur le résultat visuel, mais vous permettra par contre d'effectuer des optimisations non négligeables. En effet, il vaudra toujours mieux afficher ces faces de la plus proche (de la caméra) à la plus éloignée. Cela permet d'éliminer plus tôt les pixels cachés à l'aide du test de profondeur.
Pour les faces transparentes, il faudra par contre les trier et les afficher de la plus éloignée à la plus proche, et ceci après avoir affiché toutes les faces opaques. C'est cette fois une obligation : en effet si vous n'affichez pas vos faces transparentes dans le bon ordre vous n'aurez pas le résultat escompté à l'écran (si vous affichez ce qui se trouve derrière une face transparente après celle-ci, cela sera éliminé par le test de profondeur et n'apparaîtra tout simplement pas à l'écran).
Dans une application 3D, il existe plusieurs goulots d'étranglement possibles dans votre code. Voici un bref descriptif des plus courants, ainsi que les méthodes pour les identifier et les éviter.
Le CPU
Il s'agit de la limitation la plus courante. La cause peut en être variée, mais ce qui est sûr c'est que cela n'a aucun rapport avec le rendu 3D. Par exemple dans un jeu, cela peut provenir des calculs de physique ou d'IA, qui sont généralement très coûteux.
Le meilleur moyen de déceler les parties de votre programme qui consomment le plus de CPU, est d'utiliser un profiler. Ce genre d'outil permet d'analyser chaque fonction de manière très précise (temps d'exécution, nombre d'appels, pourcentage de CPU utilisé…). L'un des plus connus est Intel VTune.
La bande passante
Ce type de limitation est causé par de trop nombreux échanges entre la mémoire centrale et la mémoire vidéo. Cela peut arriver notamment si vous manipulez trop de données dynamiques : vertices, textures, récupération du back-buffer… Pour savoir si c'est bien la bande passante qui vous limite, vous pouvez tenter de réduire la taille des données que vous envoyez : moins d'informations dans les vertices, textures compressées ou plus petites…
La géométrie
Cette limitation est causée par des calculs trop importants sur la géométrie (transformation, éclairage, vertex shaders). En pratique cela arrive assez rarement, du fait de la puissance de calcul énorme des cartes graphiques actuelles. Pour détecter cette limitation, il suffit de diminuer le nombre de vertices envoyés à la carte graphique, ou encore de diminuer le nombre de lumières utilisées.
Le fillrate
Ce que l'on appelle fillrate est le nombre de pixels affichés par seconde. Une limitation en framerate pourra donc être causée par tout ce qui touche directement au rendu des pixels : pixel shaders, texturage, blending… Pour le vérifier, il existe un moyen simple : diminuer le nombre de pixels à afficher, en baissant la résolution.
Les shaders
Les vertex/pixel shaders sont habituellement suffisamment simples pour ne pas causer de perte de performance, mais attention toutefois : avec les nouvelles versions et la diversité des instructions disponibles, il est maintenant facile d'écrire des shaders très compliqués et peu optimisés. Pour voir si l'utilisation de shaders pénalise votre application, il suffit de les désactiver. Si c'est effectivement le cas, essayez de les optimiser ou de les simplifier.
Les textures
Une application qui utilise les textures intensivement pourra être limitée par celles-ci (notamment au niveau des transferts mémoire ou du filtrage). Tentez de réduire leur taille, d'utiliser un format de pixel plus petit, ou encore un format compressé.
Il est important de garder en tête que vous n'aurez probablement pas affaire à une seule limitation à la fois, mais constamment à une multitude. De plus, vous aurez parfois l'impression d'en éliminer une, mais vous ne l'aurez en fait que déplacée ailleurs. Il est donc important de régulièrement tester les différentes limitations (avec les quelques méthodes décrites précédemment), afin de toujours comprendre ce qui se passe au sein de votre application et ce qui affecte ses performances.
Il existe globalement deux méthodes pour effectuer des captures d'écran dans une résolution supérieure à celle de votre application.
1. La première est de créer une surface de rendu de la taille souhaitée, de rediriger votre rendu dessus, puis de sauvegarder celle-ci dans un fichier. Inconvénient : cela demande énormément de mémoire vidéo, mémoire qui n'est pas toujours disponible.
2. La seconde est de découper la scène en plusieurs morceaux ayant la résolution actuelle, puis de recoller ensuite les morceaux pour obtenir l'image finale. Inconvénients : cela nécessite de calculer les matrices de projection correspondant aux N vues à rendre, et surtout d'afficher la scène N fois.
Il existe deux techniques complètement différentes pour animer des modèles 3D. Chacune a ses avantages, ses inconvénients, et sera plus ou moins appropriée pour chaque type d'application.
L'animation par key-frames (positions clés)
Technique assez basique dans son principe : le modèle contient non pas une liste de ses triangles, mais plusieurs, chacune correspondant à une position clé de l'animation. Ainsi pour animer le modèle, il suffira de se faire succéder les positions les unes à la suite des autres, en effectuant une interpolation entre deux positions afin de ne pas avoir de mouvement saccadé. En gros on peut voir ça comme l'animation d'un film, dans lequel l'illusion de mouvement est donnée par la succession des images.
Les avantages sont donc une relative simplicité de chargement et d'utilisation, l'inconvénient étant une utilisation mémoire décuplée. Un autre inconvénient est une inexactitude potentielle dans les mouvements (l'interpolation ne produit pas toujours le résultat attendu).
Les formats les plus courants utilisant ce procédé sont les MD2 et MD3 (formats utilisés par Quake 2 et 3).
L'animation squelettale
La technique est cette fois plus compliquée, mais proche de la réalité physique : l'animation du modèle se fait via son squelette. Chaque sommet du modèle est connecté à un ou plusieurs os (bone) du squelette selon un certain poids ; différentes transformations (principalement translations et rotations) sont ensuite appliquées aux os du squelette, bougeant de manière réaliste les sommets qui leur sont rattachés et donc le modèle.
Avantage : des mouvements plus réalistes, et surtout une possibilité d'interactions physiques quelconques (contrairement à l'animation par key-frames qui est figée). Par contre, cela sera plus difficile à mettre en œuvre, et probablement aussi plus lent.
Le format MD5 (Doom 3) utilise notamment cette technique, ainsi que le format X de DirectX.
Pour un comparatif des différents formats de modèles 3D disponibles, voir https://jeux.developpez.com/faq/3d/?page=techniques#TECHNIQUES_format_modele.
Le choix du format des modèles 3D est bien souvent problématique : beaucoup de formats différents existent, mais il est difficile de trouver celui qui correspondra le mieux à ce que l'on veut en faire. Voici donc un petit comparatif des formats les plus intéressants à l'heure actuelle. À noter que nous ne parlerons pas ici des formats spécifiques à certaines API (XGL pour OpenGL, X pour DirectX) ; pour celles-ci, référez-vous aux FAQ correspondantes.
Collada
À l'origine conçu par Sony en tant que format standard pour la PSP et la PS3, Collada continue d'évoluer via le consortium Khronos, et est d'ores et déjà supporté par 3DS Max, Maya, Blender et SoftImage XSI. En fait, Collada est à la base un format XML conçu afin de pouvoir transporter toutes sortes d'informations entre les différents logiciels de 3D, sans perte. Ainsi c'est un format très riche : géométrie, skinning, morphing, animations… Mais également shaders (il s'agit du seul format XML permettant d'exporter des shaders) et données physiques.
Collada est donc un format complet (c'est son but), pouvant être utilisé avec beaucoup de logiciels manipulant des données 3D. Cependant, il n'y a, à notre connaissance, encore aucune bibliothèque développée pour tirer parti de ce format au niveau programmation.
-> Site officiel
-> Site officiel Khronos
X3D
X3D (eXtensible 3D) est un format 3D basé sur une syntaxe XML. Développé par le consortium Web3D dans le but de succéder au VRML, il est plus simple et sera bien mieux supporté que ce dernier, de par sa syntaxe XML.
Il existe déjà des plugins d'export pour les modeleurs les plus courants : 3DS Max, Blender, Maya, etc.
Bien que conçu pour la 3D sur le net, rien ne vous empêche d'utiliser ce format si votre application y est adaptée.
-> Spécifications du format
3DS
3DS est à la base le format exporté par 3D Studio Max, mais il est rapidement devenu un incontournable et géré par tous les modeleurs 3D. Très complet (géométrie, matériaux, animation squelettale, caméras, lumières…), le format 3DS est cependant difficile à appréhender et à manipuler. On peut trouver de nombreuses bibliothèques gratuites pour cela (notamment la célèbre lib3ds), mais son utilisation reste toutefois assez difficile.
À conseiller aux non-débutants, souhaitant un format riche en informations.
-> Page de la lib3ds
-> Tutoriel sur Gamedev
-> Tutoriel NeHe (OpenGL)
MD2, MD3
Formats utilisés par les moteurs Quake 2 et 3, les fichiers MD2 et MD3 ont l'avantage d'être très simples à manipuler. Étant largement répandus, on peut également trouver de nombreux outils ou modèles gratuits pour ces formats.
Inconvénient : ils gèrent tous deux les animations, mais via la technique des positions clés (keyframes), qui est une technique certes simple, mais très limitée et lourde en occupation mémoire.
Ainsi, ces formats sont souvent conseillés aux débutants.
-> Tutoriel complet MD2
-> Les spécifications du format MD2
MD5
MD5 est le format créé pour Doom 3. Contrairement aux formats MD2/MD3, il gère les animations squelettales. Étant entièrement en mode texte il est relativement facile à manipuler, par contre sa structure est un peu plus complexe à appréhender. Autre avantage : il sépare la géométrie et les animations, ainsi on peut créer autant d'animations différentes que l'on souhaite sans avoir à toucher au fichier contenant la géométrie du modèle.
-> Tutoriel complet MD5
OBJ, ASC, ASE…
Il s'agit de formats ASCII (i.e. au format textuel, facilement lisibles et donc manipulables) supportés par la plupart des modeleurs 3D, mais ne comportant bien souvent que le strict minimum (pas de données relatives à l'animation, par exemple).
On pourra également trouver beaucoup de codes/bibliothèques/outils ainsi que des modèles gratuits pour ces formats.
Idéal donc pour les débutants, ou les gens ne souhaitant gérer que le strict minimum.
-> Description, liens et modèles pour le format OBJ
-> Description, liens et modèles pour le format ASE
-> Spécifications du format ASE
CSM, CSF (Cal3D)
Les formats CSM et CSF ne vous disent peut-être rien, il s'agit en fait du format utilisé par la bibliothèque Cal3D. Cal3D est une bibliothèque C++ sous licence GPL/LGPL, permettant de gérer de manière poussée des modèles animés et ceci indépendamment de l'API 3D et de la plateforme. Son utilisation est plutôt simple, et l'on peut arriver très rapidement à des résultats impressionnants à moindre effort.
Côté export, le format Cal3D est supporté (via plugin) par les modeleurs 3D les plus courants : 3DS Max, Blender…
Cal3D est donc une solution intéressante, à la fois simple et performante. À essayer !
-> Site officiel
Pour donner votre avis ou avoir des informations complémentaires sur ces formats, vous pouvez consulter la discussion correspondante sur le forum « 2D, 3D et Jeux ».
Enfin, si vous êtes à la recherche de modèles 3D gratuits, vous pouvez consulter la page Médias gratuits de la rubrique 2D/3D/Jeux.
Lien : DirectX : quel format de modèle 3D choisir ?
Lien : OpenGL : comment charger et afficher un modèle 3D ?
Lien : Comment animer un modèle 3D ?
Pour faire des textures à trous, il faut utiliser l'alpha testing (test alpha). Il s'agit d'un test, effectué par le hardware sur chaque pixel, déterminant si un pixel doit ou non être affiché selon la valeur de sa composante alpha. Le test est simple :
Si Pix.alpha OP Ref alors
Pix visible
sinon
Pix rejeté
Pix est le pixel à tester, Pix.alpha sa composante alpha.
OP est la fonction de comparaison, que l'on peut définir via l'API 3D : égal, différent, inférieur…
Ref est la valeur de référence avec laquelle on va comparer chaque pixel. Il s'agit d'une valeur entre 0 et 255, également modifiable via l'API 3D.
Pour rejeter les pixels dont l'alpha vaut 0 par exemple, il faudra donc utiliser la fonction de comparaison « supérieur », et 0 comme référence.
Pour utiliser le test alpha, il faut donc avoir des textures possédant un canal alpha, celui-ci pouvant être obtenu soit en chargeant une image avec canal alpha (comme les PNG ou TGA), soit en spécifiant la valeur alpha au chargement (par exemple en mettant celle-ci à 0 pour tout pixel de telle couleur).
Attention néanmoins à l'utilisation de l'alpha test, car on ne peut pas récupérer les informations de géométrie pour effectuer certains autres effets (éclairage, ombrage, ou tout autre effet ayant besoin des informations de géométrie).
Dernière remarque : le même résultat pourrait être obtenu avec un certain paramétrage d'alpha-blending (voir https://jeux.developpez.com/faq/3d/?page=definitions#DEFINITIONS_alphablending), mais ce serait moins performant, car les pixels invisibles seraient alors écrits inutilement au lieu d'être rejetés.