Skip to content

D’où viennent les trous dans les maillages générés ​

Par Oleg Sidorkin, CTO et cofondateur de Cinevva

L’emplanture de l’aile d’un oiseau généré avant et après le correctif : à gauche, un rabat gris traversé de fines ouvertures vert vif ; à droite, un pli net entre l’aile et le corps
Même image, même générateur, mêmes réglages. À gauche, le résultat livré jusqu’à cette semaine. À droite, celui qui est désormais livré.

J’ai généré un oiseau avec TRELLIS.2, je lui ai appliqué un rig, et dès que l’animation s’est lancée, des trous sont apparus sous les deux ailes. De fines ouvertures lumineuses permettaient de voir directement à travers le corps. Nous avons d’abord accusé le système de rigging, qui avait bien son propre problème, mais les trous étaient déjà présents dans le maillage avant même qu’un os ne le touche. Cet article retrace l’enquête qui a suivi : ce que sont réellement ces trous, les neuf solutions qui n’ont pas fonctionné, pourquoi elles ont échoué et celle qui a réussi. J’inclus volontairement les fausses pistes. La plupart sont les solutions évidentes que l’on essaierait en premier, et chacune nous a appris quelque chose dont dépendait le correctif final.

Première étape : se mettre d’accord sur ce qu’est un trou ​

Notre vérificateur de maillage signalait zéro arête frontière, et un test de parité des rayons indiquait que la surface était fermée. J’ai donc dit à l’équipe que le maillage était propre. Ce n’était pas le cas, et la personne qui avait entouré les fentes en rouge avait raison d’insister. Un maillage peut être fermé tout en comportant des tunnels. La seule mesure qui les détecte est la caractéristique d’Euler, V moins E plus F, qui donne le nombre d’anses par composante connexe. L’oiseau en avait 69. Chaque paroi de l’export était doublée : une fente dans la paroi extérieure reliée à la cavité située derrière constitue donc une anse, et non une ouverture. Aucun outil recherchant des arêtes ouvertes ne pourra jamais la détecter.

Deuxième leçon, plus embarrassante : j’ai passé un certain temps à sonder les pixels sombres pour trouver les trous, parce que les trous sont sombres, jusqu’à ce qu’on me dise de commencer par regarder l’image. Les trous étaient lumineux. C’étaient de fines portions de la texture verte de la paroi opposée, visibles à travers un pli gris. Un algorithme que vous n’avez pas dirigé vers la bonne chose ne vous apprend rien, et le moyen le moins coûteux de bien le diriger reste de regarder.

Deuxième étape : trouver l’étape qui les crée ​

TRELLIS.2 construit un maillage en trois étapes. Un décodeur produit une surface brute à une résolution de 1024 au cube. Une passe de dual contouring à bande étroite la remaille. Un décimateur la réduit ensuite à cent mille faces, puis la texture est précalculée. Nous avons exporté la géométrie après chaque étape et projeté à chaque fois le même faisceau de rayons à travers l’emplanture de l’aile.

Tout commence dans la surface brute. C’est un ensemble de feuilles ouvertes : 266 242 boucles frontières, dont la plupart correspondent à un seul quadrilatère manquant, et 65 371 arêtes non-manifold. Au niveau de l’emplanture de l’aile, un tiers des rayons de test la traversaient directement. Le remplissage de trous exécuté ensuite ne referme presque rien : 144 faces sur cinq millions. Le remaillage travaille sur un champ de distance non signé, parce qu’il n’a pas le choix. Il ne dispose d’aucune notion d’intérieur ou d’extérieur. Il enveloppe donc chaque feuille d’une fine coque décalée. C’est pourquoi chaque paroi est doublée, avec un espacement d’environ quatre voxels, et c’est ainsi que chaque bande poreuse devient un pli traversé d’une fente. La décimation aggrave ensuite le problème de manière aléatoire : le même maillage brut, décimé deux fois, a produit respectivement 69 et 107 tunnels.

L’oiseau avec une carte thermique de la densité des arêtes ouvertes : le col, les deux emplantures d’ailes, les hanches et les extrémités des plumes de la queue sont lumineuses, tandis que le corps et la tête sont propres
Densité des arêtes ouvertes dans l’extraction brute. Elle n’augmente qu’aux jonctions entre les parties : col, emplantures des ailes, hanches et extrémités de la queue. Le corps et la tête sont propres.

Cette carte a constitué notre premier véritable indice. La porosité n’est pas aléatoire. Elle apparaît à chaque jonction entre deux parties, et nulle part ailleurs.

Les solutions qui n’ont pas fonctionné ​

Nous les avons essayées à peu près dans l’ordre de leur évidence.

tentativerésultat
résolution de 1536 au lieu de 1024pire sur trois des cinq modèles de test. Une résolution supérieure restitue davantage de contacts fins, chacun créant une nouvelle jonction
la chaîne de nettoyage intégréedavantage d’arêtes non-manifold, pas moins
remplissage de chaque petit trou avec MeshLib266 000 boucles remplies, emplanture de l’aile inchangée. La peau n’y avait jamais été générée, il n’y avait donc aucune boucle à remplir
remaillage signé à partir d’un nombre d’enroulementfait éclater le maillage en milliers de composantes. La sortie brute ne possède aucun intérieur auquel attribuer un signe
bande de contouring plus large, de 2 et 3 voxelsmoins de tunnels au total, mais la fente s’élargit au lieu de se rétrécir. Les petits tunnels fusionnent pour en former de grands
fermeture morphologique de la structure voxeliséesolidifie la coque ; le décodeur produit ensuite une surface dans chaque voxel intérieur, quatre fois plus de faces et 969 tunnels
remaillage voxel global du maillage finalgenre 0 et fente refermée. Mais aussi toutes les arêtes de plumes adoucies. Rejeté au premier regard, à juste titre
modification du seuil de croisement du décodeurde 65 000 à 58 000 arêtes ouvertes. Du bruit
ajout des voxels voisins manquants560 cas dans tout l’oiseau. Du bruit
Quatre colonnes : l’oiseau d’origine et trois versions remaillées en voxels avec un rayon croissant, l’emplanture de l’aile, un zoom de facteur dix, une tranche en coupe, l’oiseau entier et une vue du dessous
La fermeture globale. La troisième colonne referme la fente et maintient les doigts de patte séparés, mais elle arrondit aussi chaque pli de l’oiseau. Voilà à quoi ressemble une géométrie « floue ».

La fermeture globale mérite qu’on s’y attarde, car c’est la première solution qu’un modeleur essaierait : effectuer un remaillage voxel à une taille légèrement supérieure à celle de l’interstice. Topologiquement, cela fonctionne. Mais c’est aussi une représentation mensongère du modèle. Le générateur a produit des bords de plumes nets, que nous sacrifierions pour obtenir une emplanture d’aile fermée. Deux des neuf tentatives ont réduit le nombre de tunnels, et toutes deux ont été rejetées dès que quelqu’un a regardé l’oiseau plutôt que le chiffre.

Lecture du code ​

À ce stade, la seule démarche honnête consistait à arrêter de traiter le décodeur comme une boîte noire et à lire comment sa sortie était transformée en maillage. TRELLIS.2 utilise une représentation appelée O-Voxel, et le code d’extraction est court. Chaque voxel actif contient un sommet dual et trois logits, un par arête de grille partant de son coin, chacun signifiant « la surface traverse cette arête ». Un logit supérieur à zéro produit un quadrilatère reliant les quatre voxels autour de cette arête. Si l’un des quatre manque, le quadrilatère est silencieusement supprimé. C’est tout. Il n’existe aucun champ de signes, aucune occupation, aucune notion d’intérieur ou d’extérieur dans le modèle.

Nous avons modifié le décodeur pour extraire ces logits avant l’application du seuil, sur le même oiseau, puis nous les avons comptés.

Voisins manquants : 560 sur 2,7 millions de voxels. Logits proches de zéro : 8 600 sur 8 millions. Ni les uns ni les autres ne sont à l’origine du problème. Celui-ci apparaît lorsque l’on examine les quatre arêtes entourant chaque face de la grille. Une surface fermée traverse la frontière d’une face un nombre pair de fois. Sur 126 542 faces, soit 2,3 % d’entre elles, les quatre indicateurs sont incohérents, et chaque face impaire constitue par définition une arête ouverte ou non-manifold. Et cette incohérence est exprimée avec assurance. L’indicateur isolé d’une face ne comptant qu’un seul croisement possède un logit de +10, tandis que ses voisins sont à -4. Le coût médian pour inverser un indicateur et restaurer la parité est de 2,3 logits. Il ne s’agit pas d’un seuil légèrement décalé. Le modèle est certain de l’existence d’une surface qui ne peut pas se fermer.

Trois colonnes sur fond sombre : l’oiseau exporté, la sortie brute du décodeur et une version dont les signes ont été réparés. Avec des zooms de facteur dix et trente, la jonction de l’aile forme une bande déchiquetée et dentelée dans les deux versions brutes
La sortie brute du décodeur au niveau de l’emplanture de l’aile ; les zones sombres sont transparentes. La jonction ne souffre pas de quadrilatères manquants. C’est un peigne.

Nous avons ensuite mesuré la bande elle-même, exactement au point de la surface que notre caméra fixe observait depuis une semaine. Dans un rayon de douze voxels autour de ce point, il y a 2,1 fois plus de voxels actifs que n’en nécessiterait une seule feuille plane. Sur le poitrail, à titre de comparaison, ce rapport est de 1,2. Le dessous de l’aile et le corps sont deux peaux qui occupent la même couche d’un voxel. La représentation ne dispose que d’un sommet par voxel et ne peut donc pas contenir les deux. Pendant l’entraînement, la vérité terrain d’un tel voxel est une moyenne des moindres carrés des deux peaux, accompagnée d’indicateurs « n’importe quel croisement », ce qui est intrinsèquement incohérent. Le modèle a donc fidèlement appris à produire un peigne à cet endroit. Le remaillage enveloppe ce peigne dans une plaque, et cette plaque comporte des tunnels.

La largeur de la bande découle de la géométrie : environ un voxel divisé par la tangente de l’angle de contact. Deux surfaces qui se rencontrent à quatre-vingt-dix degrés produisent un pli d’un voxel et ne posent aucun problème. Une aile plaquée contre un corps, plume contre plume, des doigts de patte posés au sol : toutes ces configurations produisent une bande large de dix à quinze voxels. Voilà l’explication de la carte de porosité.

J’ai bien essayé la réparation théoriquement correcte : déduire pour chaque coin un signe intérieur/extérieur expliquant au mieux les indicateurs, puis recalculer ceux-ci. Cela ne peut pas fonctionner. Le champ de signes n’existe que sur la coque d’un voxel produite par le décodeur, et toute extension au-delà de cette coque diverge. Douze itérations ont doublé le nombre de voxels en produisant des données inutilisables.

Ce que ferait un modeleur ​

Un modeleur ne laisserait jamais deux peaux séparées par un interstice infime. Il fusionnerait l’aile avec le corps et conserverait un pli. La fermeture globale fait cela partout, et c’est précisément le problème. Il faut donc ne le faire qu’aux endroits où se trouvent les deux peaux.

Le détecteur repose sur une idée simple. Comptez les voisins actifs de chaque voxel dans un rayon de quatre voxels. Sur une feuille unique, ce nombre est identique partout et correspond à la médiane. Lorsque deux feuilles occupent la même couche, il est environ deux fois plus élevé. Marquez tout ce qui dépasse 1,5 fois la médiane, étendez la zone marquée d’un voxel, puis placez un cube unitaire à chaque emplacement. L’espace entre les peaux devient solide, la passe de contouring ne voit plus qu’une paroi et rien d’autre sur le modèle n’est modifié.

Une zone recadrée autour de l’emplanture de l’aile, traitée de trois façons : telle quelle, avec remplissage de la zone à deux feuilles dans un rayon de trois, puis dans un rayon de quatre. Des points rouges indiquent les voxels remplis le long de la jonction. La jonction dentelée devient un pli lisse
Preuve de concept sur une zone recadrée, avant toute modification du pipeline. Le rouge indique les voxels remplis. La jonction dentelée devient un pli et les plumes alentour ne bougent pas.

Nous avons d’abord testé cette approche sur une zone recadrée, avec MeshLib à la place du contouring GPU. Cinquante-trois composantes et dix-neuf anses sont devenues deux composantes et cinq anses. Nous l’avons ensuite intégrée au véritable pipeline, entre le décodeur et l’export, puis avons fait passer le même oiseau par le véritable système de remaillage et le véritable précalcul de texture.

Quatre colonnes montrant d’abord la texture, puis la géométrie sans matériau et enfin une coupe : l’original, la même sortie du décodeur exportée telle quelle, puis le remplissage à deux feuilles avec des rayons de trois et quatre. Le rabat gris et les fines ouvertures vertes disparaissent dans les deux dernières colonnes
Le véritable pipeline. Les première et deuxième colonnes utilisent la même sortie du décodeur, avec et sans décimation aléatoire. Les troisième et quatrième utilisent le remplissage.
tunnelsarêtes non-manifoldrabat à l’emplanture de l’ailetrou dans le dos
tel quel79.5318présentprésent
remplissage, rayon 384.597disparudisparu
remplissage, rayon 47238disparudisparu

Le rabat a disparu. Un deuxième trou dans le dos de l’oiseau, au niveau de la même jonction vue de derrière, que je ne cherchais même pas à corriger, a lui aussi disparu. Les plumes restent séparées, les doigts de patte restent séparés et la texture est précalculée normalement, car les cubes se trouvent à l’intérieur de la coque, là où le volume d’attributs contient déjà des données. Le traitement ajoute six secondes à une tâche de quatre minutes. Il est maintenant activé par défaut, avec un rayon de quatre.

L’oiseau entier vu de face, de dos, de dessous avec sa texture, puis de dessous en géométrie sans matériau, dans quatre colonnes : l’original, tel quel, remplissage avec un rayon de trois, remplissage avec un rayon de quatre
L’oiseau entier. Le trou blanc dans le dos, visible dans les première et deuxième colonnes, correspond à la même jonction vue de derrière. Tout ce que le générateur avait réussi est toujours présent dans les troisième et quatrième colonnes.

Ce que ce correctif ne résout pas, et ce qu’il nous apprend ​

Le nombre de tunnels a à peine changé, et je tiens à expliquer honnêtement pourquoi. Les tunnels restants se trouvent à l’extrémité des plumes et des doigts de patte, là où une partie fine se termine et où la double paroi se pince pour se refermer. Ils sont petits, invisibles et résultent d’un mécanisme différent. Le remplissage cible les contacts entre les différentes parties, précisément là où se trouvaient les trous visibles. La vraie correction doit se faire en amont. Un décodeur qui prédit les signes par sommet au lieu d'indicateurs par arête serait cohérent en matière de parité par construction. C'est ainsi que fonctionnait la génération précédente de ce modèle, et c'est pourquoi ses maillages étaient étanches. Cela nécessite de modifier l'entraînement, ce n'est pas quelque chose que nous pouvons corriger lors de l'inférence. En attendant, deux enseignements pratiques découlent de la formule des bandes. Si vous générez un personnage, une pose où les membres sont écartés du corps produira un maillage plus propre qu'une pose où ils sont plaqués contre lui. Et si vous développez à partir d'un générateur de ce type, évaluez vos maillages à l'aide de la caractéristique d'Euler et examinez le résultat de vos propres yeux avant de vous fier à un chiffre. Le chiffre disait que tout était propre. Il s'est trompé deux fois.

Le décodeur instrumenté, les exports des différentes étapes, l'analyse de parité et le rebouchage font désormais tous partie de notre pipeline. Ainsi, le prochain modèle qui produira des trous recevra le même traitement en quelques minutes plutôt qu'en une semaine.