Première leçon de régime pour vos modèles 3D : les trois outils de compression de sommets
Dans l'article précédent, nous avons ouvert un fichier GLB et constaté que les textures engloutissent 80 % du volume, tandis que les sommets n'en représentent que 10 à 20 %. La compression des sommets semble donc anecdotique, non ?
C'est tout le contraire. Lorsque les textures d'un modèle sont déjà compressées en KTX2 et que les sommets sont d'une densité extrême, les 20 % restants sont précisément les sommets — et ces 20 % peuvent encore être réduits de moitié, voire de 90 %. Plus important encore, la compression des sommets est l'une des rares optimisations quasi gratuites et immédiatement efficaces : quelques lignes de commande, un changement de décodeur, et le fichier s'allège.
Cet article clarifie trois choses : à quoi ressemblent réellement les données de sommets ; les caractéristiques respectives de la quantification, de MeshOpt et de Draco ; et une conclusion qui vous évitera bien des pièges — il n'existe pas de solution « universelle », seulement des solutions « adaptées ».
Combien pèse réellement un sommet
Regardons d'abord ce que contient un sommet. Dans glTF, chaque sommet est composé de plusieurs attributs :
| Attribut | Rôle | Précision par défaut | Octets par sommet |
|---|---|---|---|
| position | Coordonnées dans l'espace | 3 × float32 | 12 |
| normal | Détermine la direction de l'éclairage | 3 × float32 | 12 |
| tangent | Calcul des normal maps | 4 × float32 | 16 |
| texcoord_0 (UV) | Coordonnées d'échantillonnage des textures | 2 × float32 | 8 |
| color | Coloration au niveau du sommet | 4 × float32 | 16 |
Un sommet avec tous les attributs PBR complets, c'est 48 à 64 octets de données géométriques pures. Pour un modèle de 100 000 sommets, cela représente 5 à 6 Mo rien que pour les sommets.
Remarquez que presque tout utilise du float32 (nombre flottant 32 bits). C'est la configuration par défaut, et c'est aussi la porte d'entrée de la compression des sommets — car la grande majorité des attributs n'ont pas besoin d'une précision de 32 bits.
Premier outil : la quantification
La quantification est le principe sous-jacent de toute compression de sommets. Draco et MeshOpt l'utilisent également en interne.
La quantification (transformer des nombres flottants haute précision en entiers basse précision) fonctionne ainsi : pour le flottant 3,14159265, retenir 3,14 suffit amplement. Pour un ensemble de coordonnées dans un espace donné, on n'enregistre plus chaque décimale en 32 bits, mais on représente la valeur avec un entier sur une plage réduite.
Original : position.x = 1.234567 (float32, 4 octets)
Quantifié : position.x = 1234 (int16, 2 octets) + un scale/offset pour reconstruire
Comparaison avant/après quantification :
| Attribut | Octets float32 | Après quantification (16 bits) | Économie |
|---|---|---|---|
| position | 12 | 6 | 50 % |
| normal | 12 | 6 (ou 4, avec int8 + octaédrique) | 50-67 % |
| tangent | 16 | 4-8 | 50-75 % |
| texcoord | 8 | 4 | 50 % |
Pour le sommet de 48-64 octets évoqué plus haut, la quantification permet de descendre à 16-24 octets, soit plus de la moitié du volume en moins.
Quand utiliser la quantification
- Vous voulez réduire le poids sans chercher un taux de compression extrême
- Vous souhaitez zéro dépendance de décodeur — un glTF quantifié utilise l'extension standard
KHR_mesh_quantization, nativement supportée par les principaux moteurs, sans bibliothèque supplémentaire - La plateforme cible est sensible à la taille du package (par exemple, une mini-application, où ajouter un décodeur Draco représente des dizaines de Ko)
Quand l'éviter
- Le modèle est très petit et le détail est essentiel (pièces industrielles au millimètre près). La quantification est particulièrement traître sur les petits modèles : les textures passent, mais un décalage de position de 0,1 mm devient visible à l'œil nu en plan rapproché.
Le vrai piège de la perte de précision : dans une scène de présentation de bijoux, après quantification d'un modèle de bague en 16 bits, des crénelures sont apparues sur les bords métalliques en gros plan. La cause n'était pas un manque de sommets, mais un système de coordonnées trop petit : la plage d'entiers 16 bits ne permettait pas une granularité suffisante. La solution : réduire la plage de quantification (rétrécir la bounding box de
position) ou passer à une profondeur de bits supérieure pour les petits modèles.
Deuxième outil : MeshOpt
MeshOpt est l'extension officielle glTF EXT_meshopt_compression, positionnée comme « bon taux de compression, décodage ultra-rapide ».
Son approche : d'abord quantifier les attributs (comme ci-dessus), puis appliquer une technique appelée LISS (lossless entropy coding, codage entropique sans perte) pour compresser davantage les entiers quantifiés, sans perte supplémentaire. En d'autres termes : quantification avec perte + codage entropique sans perte = volume réduit, qualité identique à la quantification seule.
- Taux de compression : 30 à 50 % de moins que la quantification seule
- Vitesse de décodage : extrêmement rapide, implémentation pure C/JS, des dizaines de millions de sommets par seconde sur un seul thread
- Taille du décodeur : très petite (environ 20-30 Ko gzip)
- Compatibilité : support natif dans Three.js et Babylon.js, standard de facto sur le Web
Quand utiliser MeshOpt
- Vous avez besoin d'un meilleur taux de compression sans accepter le décodage plus lent de Draco
- Cible Web principalement, mobile, WebXR — la vitesse de décodage impacte directement l'expérience de chargement initial
- Le modèle doit être décompressé fréquemment (niveaux chargés dynamiquement)
Quand l'éviter
- Votre plateforme cible ne reconnaît pas
EXT_meshopt_compression(rares moteurs anciens) - Vous avez juste besoin que « ça marche » et ne vous souciez pas d'un écart de 30 % — la quantification pure est plus simple, une dépendance en moins
Troisième outil : Draco
Draco est la solution de compression développée par Google, positionnée sur le « taux de compression extrême ».
La différence fondamentale avec les deux précédents : Draco modifie la connectivité des sommets (la topologie). La quantification ne change que la représentation numérique de chaque sommet, MeshOpt ajoute un codage sans perte par-dessus, mais Draco réorganise le maillage triangulaire pour exprimer de manière plus compacte « quels sommets forment quels triangles ».
- Taux de compression : le plus élevé des trois, souvent plus de 90 % de réduction sur les modèles denses
- Vitesse de décodage : la plus lente des trois, mais reste rapide (relativement)
- Taille du décodeur : plus importante (environ 100-200 Ko, généralement chargé en wasm séparément)
- Qualité : réglable, mais des déformations visibles apparaissent aux taux extrêmes
Quand utiliser Draco
- Modèles très volumineux, sommets ultra-denses (scans de millions de sommets, terrains)
- Chargement unique, réutilisation longue après décompression (un décodage plus lent est acceptable)
- La taille du package n'est pas le goulot d'étranglement, c'est la vitesse de téléchargement
Quand l'éviter
- Mobile + besoin d'un affichage initial rapide — le décodeur et le modèle doivent tous deux être téléchargés, ce qui ralentit au final
- Environnements stricts sur la taille du package (mini-applications)
- Modèles nécessitant de l'animation squelettique ou des morph targets — Draco les gère mal, et une mauvaise configuration peut causer des problèmes
Les trois côte à côte : un tableau pour choisir
Les taux de compression ci-dessous proviennent de benchmarks communautaires (tests de DeepKolos sur Zhihu + discussions Reddit r/threejs). Les résultats varient selon les modèles, mais les relations relatives restent stables :
| Solution | Taux de compression (vs float32) | Vitesse de décodage | Taille du décodeur | Avec perte ? | Extension glTF |
|---|---|---|---|---|---|
| Quantification pure | ~50 % | Natif, aucun décodage | 0 | Oui (précision) | KHR_mesh_quantization |
| MeshOpt | ~25-35 % | Extrêmement rapide | ~25 Ko | Oui (précision) | EXT_meshopt_compression |
| Draco | ~10-20 % | Rapide (le plus lent des trois) | ~100-200 Ko | Oui (précision + topologie) | KHR_draco_mesh_compression |
Compatibilité plateformes et décodeurs :
| Plateforme | Quantification pure | MeshOpt | Draco |
|---|---|---|---|
| Web desktop | ✅ Natif | ✅ Natif | ✅ Décodeur requis |
| Web mobile | ✅ Natif | ✅ Natif | ⚠️ Décodeur lourd |
| WebXR/VR | ✅ Natif | ✅ Recommandé | ⚠️ Prudence |
| Mini-applications | ✅ Recommandé | ✅ Recommandé | ❌ À éviter |
En résumé : pour la simplicité et zéro dépendance → quantification pure ; pour l'équilibre → MeshOpt ; pour un taux de compression extrême et si vous pouvez attendre → Draco.
En pratique : gltfpack pour la quantification et MeshOpt
gltfpack est l'outil officiel glTF. Une seule commande suffit pour la quantification et MeshOpt.
Installation (binaire téléchargeable depuis gltfpack release) :
# Quantifie model.glb en 16 bits et ajoute la compression MeshOpt
gltfpack -i model.glb -o model-packed.glb -cc
# -cc = compress (superpose EXT_meshopt_compression par-dessus la quantification par défaut)
Paramètres courants :
# Quantification seule, sans MeshOpt (le plus léger, zéro dépendance de décodeur)
# gltfpack quantifie par défaut les sommets en 16 bits (KHR_mesh_quantization), aucun paramètre supplémentaire requis
gltfpack -i model.glb -o model-quant.glb
# Quantification + MeshOpt
gltfpack -i model.glb -o model-meshopt.glb -cc
# Pour les modèles très denses, simplification possible (réduit le nombre de sommets, modifie le modèle)
gltfpack -i model.glb -o model-simplify.glb -cc -si 0.5
# -si 0.5 = simplifie à environ 50 % des sommets
À propos de
-cc: c'est l'option « compress », qui superposeEXT_meshopt_compression. Sans-cc, gltfpack applique par défaut la quantification — autrement dit, la commandegltfpack -i in.glb -o out.glbest déjà une « quantification pure, zéro dépendance de décodeur ». (-vest l'option verbose pour les logs détaillés, ne pas confondre.)
Résultats typiques (modèle PBR de 5 Mo, 120 000 sommets, à titre indicatif) :
| Traitement | Taille du fichier | Description |
|---|---|---|
| Original (float32) | 5,0 Mo | Référence |
| Quantification pure (défaut) | 2,6 Mo | Moitié, aucune différence visuelle notable |
MeshOpt (-cc) | 1,7 Mo | 35 % de moins, chargement légèrement plus rapide |
Attention : la simplification
-siest une opération avec perte qui modifie la géométrie du modèle, distincte de la compression. La compression préserve au maximum l'apparence, la simplification réduit activement les détails. Les deux peuvent se cumuler, mais selon que le scénario le permet.
Pièges courants
- Les normales changent de direction après quantification : probablement une précision trop faible. Utilisez au moins 16 bits pour les normales, ou un encodage octaédrique en 8 bits.
- Matériaux perdus après décodage Draco : Draco ne compresse que le maillage ; matériaux et textures doivent être traités séparément. Au chargement, il faut configurer à la fois le décodeur Draco et l'extension KHR.
- Draco ne se charge pas dans les mini-applications : le wasm du décodeur rencontre des restrictions dans certains environnements d'exécution. Passer à MeshOpt règle généralement le problème.
- Le modèle « dérive » après quantification : si le modèle est trop éloigné de l'origine du repère, la précision 16 bits ne suffit pas pour exprimer de grandes coordonnées avec de petits détails. Solution : rapprocher le modèle de l'origine avant quantification, ou augmenter la profondeur de bits.
Prochaine étape
Les sommets sont compressés, mais ne vous réjouissez pas trop vite — rappelez-vous que les textures représentent 80 % du volume d'un modèle. Dans le prochain article, nous changeons de champ de bataille pour voir pourquoi les PNG/JPG traditionnels sont de véritables « goinfres » aux yeux du GPU, et comment les formats de textures natifs GPU résolvent ce problème.