Three.js: GLTFLoader: le résultat du modèle de test de tangente normale est incorrect

Créé le 3 juin 2017  ·  30Commentaires  ·  Source: mrdoob/three.js

description du problème

J'ai essayé d'afficher le modèle de test de tangente normale .
Cependant, le résultat affiché semble être différent de l'échantillon de Khronos.

Résultat du modèle de test Three.js + Normal-Tangent:
image

Chargeur d'échantillons Khronos + résultat du modèle de test à tangente normale:
image

Je pense que cet exemple de modèle devrait avoir les mêmes résultats à gauche et à droite.

En relation: https://emackey.github.io/testing-pbr/normal-tangent-readme.html

/ cc @emackey

Version Three.js
  • [x] Dev
  • [] r85
  • [] ...
Navigateur
  • [x] Tous
  • [] Chrome
  • [] Firefox
  • [ ] Internet Explorer
OS
  • [x] Tous
  • [ ] Les fenêtres
  • [] macOS
  • [] Linux
  • [ ] Android
  • [] iOS
Configuration matérielle requise (carte graphique, périphérique VR, ...)

ThinkPad X260 + Windows 10 + Intel HD Graphics 520

Bug Loaders

Commentaire le plus utile

Je voudrais citer une phrase de la

Les vecteurs normaux utilisent les conventions OpenGL où + X est à droite et + Y est en haut. + Z pointe vers le spectateur.

Cette phrase est ce qui permet aux modèles d'être expédiés sans vecteurs tangents, ce qui économise de l'espace.

Testons-le. Ici, j'ai fait une carte de hauteur moche (carte de relief) à partir d'une tache dans un programme de peinture:

TestHeightMap

Définissons ce champ de hauteur comme une bosse vers l'extérieur, où les pixels blancs sont plus proches du spectateur et les pixels noirs sont plus éloignés.

En utilisant un convertisseur en ligne (de qualité douteuse, mais nous examinerons le résultat dans un instant), j'ai converti cela d'une carte de hauteur en carte normale. Gardez à l'esprit qu'il n'y a pas de modèle 3D ici, pas de coordonnées UV ou de calculs mikktspace ou de toute géométrie. Juste une carte de hauteur convertie en carte normale. J'ai dû configurer manuellement le convertisseur en ligne selon les instructions de glTF, de sorte que X a raison, Y est en place et Z fait face au spectateur. Voici le résultat:

TestNormalMap

Remettons cela dans un programme de peinture et supprimons les canaux de couleur pour voir où ces vecteurs pointent. Ci-dessous, chaque canal de couleur a été séparé en une image en niveaux de gris qui lui est propre. N'oubliez pas que ceux-ci seront interprétés de telle sorte que le noir signifie -1.0 , le gris moyen signifie 0.0 et le blanc signifie +1.0 .

TestNormalMap-Decomposed

Je pense donc que le convertisseur en ligne a fait ce que glTF a demandé, du moins après l'avoir configuré correctement. Dans l'image rouge (X), nous pouvons voir que la pente à droite a des pixels blancs (+1,0), pointant le vecteur X sur le bord droit de l'image. Sur le côté gauche de l'image rouge, les pixels noirs (-1,0) pointent le vecteur X sur le côté gauche de l'image. Dans l'image verte (Y), les pixels blancs le long de la pente supérieure du relief pointent le vecteur Y en haut de l'image. Les valeurs Z sont les moins intuitives, mais rappelez-vous que la pointe de la bosse et la plaque arrière elle-même pointent toutes les deux vers le spectateur, et les pentes de tous les côtés pointent vers l'extérieur, elles sont donc toutes uniformément plus sombres.

Et si nous chargeons ceci dans Blender Eevee, qui (tout comme glTF) accepte les normal maps de style OpenGL? Que se passe-t-il si la carte UV est pivotée ou même mise à l'échelle pour être inversée?

NormalSpinTest

Il s'avère que cela fonctionne très bien. En effet, tout l'intérêt de définir l'espace tangent de cette manière n'est pas de permettre au logiciel de devenir fou avec les vecteurs, c'est de permettre aux artistes de texture une certaine raison en s'assurant que leurs textures normales seront à droite quelle que soit la géométrie.

Mais tous les logiciels n'utilisent pas la convention OpenGL. Certains utilisent une convention différente (parfois appelée convention DirectX), où les vecteurs Y pointent vers le bas de l'image au lieu du haut. Voici le canal Y décomposé de mon image sous cette forme. Les pixels les plus clairs sont ceux qui font face au bas de l'image.

TestNormalMap_DirectX-Green

Si je charge une de ces cartes normales de style DirectX dans Blender Eevee, puis-je encore m'attendre à ce qu'elle fonctionne?

NormalSpinTest_DirectX_v3

Non. Blender s'attendait à une hausse de + Y. Le calcul est faux et la ligne d'horizon réfléchie tourne tout autour.
La même chose se produit si vous chargez une carte normale de style OpenGL dans un moteur qui attendait + Y vers le bas.

C'est ce que le modèle NormalTangentTest tente de tester. Chaque ligne fait tourner les coordonnées UV dans une orientation différente, en essayant de s'assurer que les réflexions restent à droite dans ces différentes orientations.

Tous les 30 commentaires

Merci d'avoir écrit ceci @ cx20.

Juste pour plus de contexte, voici quelques informations et problèmes connexes:

  • Je l' ai signalé un problème similaire sur @donmccurdy « s ThreeJS spectateur glTF, donmccurdy / trois gltf-viewer # 10. Mais je pense maintenant que c'est un bogue dans le chargeur glTF de ThreeJS, pas dans le visualiseur. Donc, ce nouveau numéro est mieux placé que mon ancien numéro.

  • Dans l'ancien numéro ci-dessus, le modèle faisait face au ciel, mais plus tard, je l'ai fait pivoter pour faire face à l'horizon. Cela permet de voir plus facilement quand les reflets ne sont pas corrects, car l'horizon tourne de manière folle, comme indiqué ci-dessus.

  • La propre utilisation par le modèle d'une carte normale a été confirmée correcte par une discussion dans KhronosGroup / glTF # 952.

  • La gestion par ThreeJS des cartes normales a été remise en question dans le # 11315.

  • BabylonJS a récemment eu un problème similaire, signalé ici et corrigé ici . Voici une démo en direct du chargeur glTF 2.0 de BabylonJS avec le correctif appliqué .

Bonne rédaction, merci!

Le modèle semble rendu correctement avec l'ajout de cette ligne:

materialParams.normalScale = new THREE.Vector2(-1, 1);

Mais je ne suis pas sûr de bien comprendre le problème. Comprendre # 11315 pourrait aider ici.

@donmccurdy Merci pour vos conseils.
J'ai compris que s'améliorer en ajustant normalScale.
Cependant, si le modèle glTF est correct, je pense qu'il vaut mieux que glTF Loader le gère.

@ cx20 est d' accord, ce correctif est maintenant fusionné dans THREE.GLTF2Loader.

J'ai confirmé qu'il était en train d'être corrigé.

Résultat du modèle de test Three.js + Normal-Tangent:
image

Cela a régressé, parfois entre r101 et r104:
Screenshot from 2019-06-11 16-38-27

Voir https://github.com/mrdoob/three.js/pull/15749 - la régression est intentionnelle et peut être évitée en incluant des tangentes dans le modèle.

Idéalement, nous aurions une implémentation JS pour générer des tangentes mikktspace, pour résoudre complètement cela, mais c'est assez complexe.

Je n'étais pas au courant de # 15749 jusqu'à présent. Je suis pris au dépourvu par cela, j'avais pensé que nous avions fait un assez bon travail en définissant les tangentes dans glTF pour qu'elles puissent être au moins approximées à l'exécution.

Notez que l'exportateur de Blender n'exportera aucune tangente glTF par défaut, car il permet de réduire la taille du fichier, et les principales implémentations de glTF ont toutes réussi ce test sans tangentes. Je soupçonne que ce changement a peut-être rompu le mappage normal pour la majorité des modèles glTF dans ThreeJS.

J'aurai besoin d'un peu de temps pour lire tous les problèmes liés afin de mieux comprendre ce qui s'est passé et pourquoi. Mais je pense que la communauté glTF devrait considérer cette priorité élevée pour obtenir à nouveau un rendu correct des modèles sans tangentes, car je pense que la plupart des modèles sont dans cette catégorie par défaut.

Réouverture 😅

Je soupçonne que ce changement a peut-être rompu le mappage normal pour la majorité des modèles glTF dans ThreeJS.

Je ne pense pas que ce soit quelque chose d'aussi grave - nous avons toujours généré des tangentes en temps réel dans le shader avec des dérivés, et nous le faisons toujours. Nous avons précédemment inclus un hack ( normalScale.y *= -1 ) qui a corrigé ce modèle de test spécifique, mais qui a également cassé d'autres exemples. Je n'ai aucune explication pour savoir quand cela a aidé, ou pas, alors nous avons supprimé le hack une fois que nous avons pris en charge les tangentes stockées - dans ce cas, cela aurait certainement été faux. Maintenant, les modèles qui reposaient sur le hack (et n'incluent pas les tangentes) sont cassés, et les modèles qui ont été cassés par le hack (et n'incluent pas les tangentes) sont corrigés.

Mais je pense que la communauté glTF devrait considérer cette priorité élevée pour obtenir à nouveau un rendu correct des modèles sans tangentes, car je pense que la plupart des modèles sont dans cette catégorie par défaut.

Voir au dessus. En général, je pense que nous rendons les modèles sans tangentes de manière adéquate. Cependant, nous ne générons pas de tangentes mikktspace comme l'exige la spécification glTF. À ma connaissance, aucune implémentation JS de cela n'existe, et notre implémentation de shader basée sur les dérivés est simplement une approximation "plutôt bonne". Cet exemple de modèle est un cas intentionnellement extrême qui démontre les limites de cette approximation.

Nous serions heureux d'avoir une implémentation de génération tangente JS mikktspace; ce serait un bon ajout à THREE.BufferGeometryUtils. Mais le code officiel (natif) de mikktspace est assez long, et je n'ai pas encore assez creusé pour voir combien cela est nécessaire pour générer des tangentes.

Nous avons précédemment inclus un hack ( normalScale.y *= -1 ) qui a corrigé ce modèle de test spécifique, mais qui a également cassé d'autres exemples

At-il brisé d'autres modèles glTF en particulier, ou juste des exemples en général?

Il existe deux types de cartes normales d'espaces tangents à l'état sauvage. Substance Painter les appelle «DirectX Normals» et «OpenGL Normals», ce qui n'est pas le meilleur nom pour cela. La différence est spécifiquement que le canal y est inversé, ce qui signifie que toutes les valeurs du canal vert dans la texture sont inversées. Multiplier y *= -1 est la bonne façon de convertir l'un en l'autre. Les soi-disant «normales DirectX» utilisent un système de coordonnées à gauche, et glTF définit un système de coordonnées à droite pour normal / tangent / bitangent.

Ce que je soupçonne, c'est que lorsque ThreeJS calcule automatiquement les tangentes, il s'attend à ce que la carte normale ait été créée avec le style Y inversé (DirectX), et récupère ce canal pour glTF, le retournement est donc nécessaire. Cependant, lorsque les tangentes sont fournies, un tel retournement n'est pas nécessaire.

Je pense que la question de mikktspace est distincte de cela. Il est malheureux que la spécification appelle mikktspace et que la plupart des implémentations se rapprochent de cela avec des dérivés d'espace écran. Je ne sais pas à quel point les deux sont similaires, mais les cartes normales générées dans mikktspace semblent fonctionner raisonnablement bien lorsqu'elles sont affichées avec l'approximation, à condition que la gauche / droite de la carte soit effectuée correctement.

(Il y a aussi une discussion à ce sujet depuis l'année dernière dans KhronosGroup / glTF-Sample-Models # 174)

Je voudrais citer une phrase de la

Les vecteurs normaux utilisent les conventions OpenGL où + X est à droite et + Y est en haut. + Z pointe vers le spectateur.

Cette phrase est ce qui permet aux modèles d'être expédiés sans vecteurs tangents, ce qui économise de l'espace.

Testons-le. Ici, j'ai fait une carte de hauteur moche (carte de relief) à partir d'une tache dans un programme de peinture:

TestHeightMap

Définissons ce champ de hauteur comme une bosse vers l'extérieur, où les pixels blancs sont plus proches du spectateur et les pixels noirs sont plus éloignés.

En utilisant un convertisseur en ligne (de qualité douteuse, mais nous examinerons le résultat dans un instant), j'ai converti cela d'une carte de hauteur en carte normale. Gardez à l'esprit qu'il n'y a pas de modèle 3D ici, pas de coordonnées UV ou de calculs mikktspace ou de toute géométrie. Juste une carte de hauteur convertie en carte normale. J'ai dû configurer manuellement le convertisseur en ligne selon les instructions de glTF, de sorte que X a raison, Y est en place et Z fait face au spectateur. Voici le résultat:

TestNormalMap

Remettons cela dans un programme de peinture et supprimons les canaux de couleur pour voir où ces vecteurs pointent. Ci-dessous, chaque canal de couleur a été séparé en une image en niveaux de gris qui lui est propre. N'oubliez pas que ceux-ci seront interprétés de telle sorte que le noir signifie -1.0 , le gris moyen signifie 0.0 et le blanc signifie +1.0 .

TestNormalMap-Decomposed

Je pense donc que le convertisseur en ligne a fait ce que glTF a demandé, du moins après l'avoir configuré correctement. Dans l'image rouge (X), nous pouvons voir que la pente à droite a des pixels blancs (+1,0), pointant le vecteur X sur le bord droit de l'image. Sur le côté gauche de l'image rouge, les pixels noirs (-1,0) pointent le vecteur X sur le côté gauche de l'image. Dans l'image verte (Y), les pixels blancs le long de la pente supérieure du relief pointent le vecteur Y en haut de l'image. Les valeurs Z sont les moins intuitives, mais rappelez-vous que la pointe de la bosse et la plaque arrière elle-même pointent toutes les deux vers le spectateur, et les pentes de tous les côtés pointent vers l'extérieur, elles sont donc toutes uniformément plus sombres.

Et si nous chargeons ceci dans Blender Eevee, qui (tout comme glTF) accepte les normal maps de style OpenGL? Que se passe-t-il si la carte UV est pivotée ou même mise à l'échelle pour être inversée?

NormalSpinTest

Il s'avère que cela fonctionne très bien. En effet, tout l'intérêt de définir l'espace tangent de cette manière n'est pas de permettre au logiciel de devenir fou avec les vecteurs, c'est de permettre aux artistes de texture une certaine raison en s'assurant que leurs textures normales seront à droite quelle que soit la géométrie.

Mais tous les logiciels n'utilisent pas la convention OpenGL. Certains utilisent une convention différente (parfois appelée convention DirectX), où les vecteurs Y pointent vers le bas de l'image au lieu du haut. Voici le canal Y décomposé de mon image sous cette forme. Les pixels les plus clairs sont ceux qui font face au bas de l'image.

TestNormalMap_DirectX-Green

Si je charge une de ces cartes normales de style DirectX dans Blender Eevee, puis-je encore m'attendre à ce qu'elle fonctionne?

NormalSpinTest_DirectX_v3

Non. Blender s'attendait à une hausse de + Y. Le calcul est faux et la ligne d'horizon réfléchie tourne tout autour.
La même chose se produit si vous chargez une carte normale de style OpenGL dans un moteur qui attendait + Y vers le bas.

C'est ce que le modèle NormalTangentTest tente de tester. Chaque ligne fait tourner les coordonnées UV dans une orientation différente, en essayant de s'assurer que les réflexions restent à droite dans ces différentes orientations.

Il doit encore y avoir une formule concrète dans la spécification pour savoir comment calculer une tangente pour une primitive isolée, étant donné une primitive et ses coordonnées UV, et quel signe W utiliser pour bitangente. Les «normales OpenGL» et «normales DX» ne sont pas assez précises pour dériver la formule. Ils peuvent faire référence à des conventions, mais je n'ai aucune idée en tant que responsable de la mise en œuvre de ce que je dois faire.

Ce que je fais actuellement est d'émettre TangentW retourné de MikkTSpace pour correspondre à cet échantillon particulier, mais c'est exactement ce qui a fonctionné.

At-il brisé d'autres modèles glTF en particulier, ou juste des exemples en général?

Les bogues signalés étaient liés aux modèles glTF, en particulier. Cela dit, je doute qu'il y ait suffisamment de confiance dans l'exportation de matériel via FBX ou COLLADA pour dire que ces conventions de carte normales ont été parfaitement comprises et testées non plus.

La différence est spécifiquement que le canal y est inversé, ce qui signifie que toutes les valeurs du canal vert dans la texture sont inversées. Multiplier y * = -1 est la bonne façon de convertir l'un en l'autre.

Merci, c'est une bien meilleure justification de notre "hack" que nous l'avions quand nous l'avons implémenté. 😇

Il est malheureux que la spécification appelle mikktspace et que la plupart des implémentations se rapprochent de cela avec des dérivés d'espace écran.

La spécification est juste que MikkTSpace est le moyen le plus robuste de générer des tangentes, je pense que ce n'est pas universellement le bon choix pour le faire automatiquement au moment de l'exécution. Si les alternatives moins chères semblent correctes pour un modèle particulier, il n'y a aucune raison de faire quelque chose de plus cher qui n'a pas l'air meilleur. Le langage des spécifications pourrait être assoupli pour permettre des approximations, mais je ne suis pas convaincu à ce sujet.

Il doit encore y avoir une formule concrète dans la spécification pour savoir comment calculer une tangente pour une primitive isolée, étant donné une primitive et ses coordonnées UV, et quel signe W utiliser pour bitangente.

Je ne suis pas sûr que l'algorithme de MikkTSpace soit si facile à représenter comme une formule discrète ... demandez-vous une alternative au code canonique de MikkTSpace? Ou des informations supplémentaires au-delà des instructions d'utilisation de MikkTSpace? @Themaister


Pour le problème d'origine, il semble que nous devrions restaurer le multiplicateur normal.y *= -1 quelque part. Il y a trois endroits possibles pour le faire:

  • (a) dans GLTFLoader, pour les maillages qui n'ont pas de tangentes
  • (b) dans GLTFLoader, pour tous les maillages
  • (c) dans WebGLRenderer, pour tous les maillages

Si threejs utilise vraiment la convention DirectX et par exemple Blender ne l'est pas, je pourrais voir un cas pour (c). Dans l'intérêt d'une solution rapide et sûre, cependant, je suis enclin à aller avec (a).

Ce que je soupçonne, c'est que lorsque ThreeJS calcule automatiquement les tangentes, il s'attend à ce que la carte normale ait été créée avec le style Y inversé (DirectX).

Non, three.js ne suppose pas que ...

three.js suppose que + Y est "haut" pour l'espace tangent, et l'augmentation-V est "haut" pour l'espace uv.

Autrement dit, three.js suppose que uv (0, 0) est dans le coin inférieur gauche de la texture, tandis que la spécification glTF suppose le coin supérieur gauche. C'est pourquoi GLTFLoader définit texture.flipY à false . (three.js définit texture.flipY à true par défaut.)

Lorsque les tangentes ne sont pas présentes, three.js utilise des dérivées écran-espace pour estimer les tangentes. Il le fait en utilisant la règle de la chaîne. Une hypothèse dans ce calcul est que l'espace tangent et l'espace UV ont la même orientation.

Pour les modèles glTF correctement créés, cette hypothèse n'est pas correcte. Cependant, vous devriez pouvoir compenser en définissant normalScale.y = - 1 pour tout modèle qui _ adhère correctement à la spécification glTF_.

Il me semble également que nous pourrions résoudre ce problème automatiquement en honorant le drapeau flipY dans le shader.

Si la configuration de normalScale.y ne fonctionne pas, c'est qu'il se passe autre chose.

Merci pour cette clarification. Je pense que nous avons une voie à suivre ici.

Il me semble également que nous pourrions résoudre ce problème automatiquement en honorant le drapeau flipY dans le shader.

Oui, à l'exception des cas où le glTF fournit ses propres vecteurs tangents, non? Je m'attendrais à ce que seules les tangentes calculées automatiquement aient besoin du test flipY et de la négation correspondante de y .

Si ce n'est pas le cas ... cela voudrait dire que mon modèle NormalTangentMirrorTest contient des tangentes incorrectes encodées, ce qui signifie que l'exportateur Blender glTF lui-même met les mauvaises tangentes dans les modèles glTF.

Edit: Je crois avoir confirmé l'exactitude des vecteurs tangents exportés, et ils n'ont pas besoin de basculement en Y. Je peux publier plus de détails à ce sujet si nécessaire.

Mais il semble que l'action correcte soit de retourner normalScale.y uniquement lorsque des tangentes générées automatiquement (tangentes non fournies) sont utilisées avec l'indicateur flipY . Pensées?

Dans mon article précédent, j'ai expliqué comment compenser manuellement les normales inversées lorsque les tangentes d'attribut ne sont pas présentes. Il ne devrait y avoir rien à compenser lorsque des tangentes sont présentes car les dérivées de l'espace écran ne sont pas utilisées.

J'ai également suggéré que nous pourrions être en mesure de résoudre ce problème automatiquement en honorant le drapeau flipY dans le shader. Cependant, je n'ai pas dit que nous réglerions ce problème automatiquement en retournant normalScale.y . Je ne pense pas que nous devrions modifier les paramètres de l'utilisateur.

En tout état de cause, avant de s'engager dans cette voie, je pense qu'il est impératif de vérifier cette hypothèse:

[V] ous devriez pouvoir compenser en définissant normalScale.y = - 1 pour tout modèle qui adhère correctement à la spécification glTF.

Nous devons avoir une explication pour chaque modèle qui n'est pas rendu correctement.

Je ne pense pas que nous devrions modifier les paramètres de l'utilisateur.

Oui, excusez-moi, je n'avais pas l'intention de dicter ce genre de détails de mise en œuvre. J'essaie juste de m'assurer qu'il n'y a pas de malentendu sur ce que _honorer le flipY drapeau_ signifie mathématiquement, dans le but de la génération de tangente dans l'espace écran.

Nous devons avoir une explication pour chaque modèle qui n'est pas rendu correctement.

Cela ressemble à cela pourrait potentiellement être un grand ensemble. S'il y a effectivement des modèles qui font quelque chose de radicalement différent des modèles de test officiels, et qui pourraient encore être considérés comme des utilisations valides des cartes normales dans glTF, il serait important de le découvrir. Les modèles de test sont destinés à couvrir les utilisations valides des cartes normales sur la géométrie statique (non transformée). Il ne devrait pas y avoir de moyen, en particulier lorsque l'on s'appuie sur des vecteurs tangents générés par le spectateur, de construire le modèle dans un autre système de coordonnées et de prétendre qu'il s'agit d'un glTF valide.

Je suppose que cela est étroitement lié à ce problème? https://github.com/KhronosGroup/glTF-Sample-Models/issues/174

@WestLangley Voici ce que j'ai trouvé jusqu'à présent. Par défaut, khronos-NormalTangentTest (aucune tangente incluse) ne semble pas correct:
NormalTangentTest
Si je règle normalScale.y = -1, c'est toujours faux:
NormalTangentTestNegY
Si je mets à la place normalScale.x = -1, cela semble correct:
NormalTangentTestNegX
À l'exception possible que le rendu semble un peu pixélisé, en particulier sur le réflecteur doré. Est-ce attendu avec l'approximation de l'espace écran, ou un indicateur d'un autre bogue?

Si quelqu'un a d'autres bons modèles de test sans tangentes, envoyez-les moi. Je voudrais m'assurer d'avoir un échantillon représentatif et déterminer s'il y en a qui ne correspondent pas aux spécifications glTF.

@elalish Ce banc d'essai n'a pas de sens pour moi, cela n'a jamais eu de sens pour moi et cela ne le sera probablement jamais. Alors ... je ne vous aiderai pas à essayer de l'expliquer.

Cet exemple est trop obscur à mon humble avis. Nous avons besoin d'un cas de test cohérent qui fournit suffisamment de visuels pour comprendre ce qui se passe.

BTW, nous avions l'habitude d'avoir un VertexTangentHelper (# 3511), qui pouvait être réactivé pour prendre en charge la géométrie du tampon.

@WestLangley J'aurais aimé savoir comment rendre NormalTangentTest plus clair. Il ne fait rien d'extraordinaire, il fait simplement pivoter l'orientation (dans l'espace UV) de chaque échantillon. Les trois colonnes sont juste pour tester différents matériaux, la colonne d'or pourrait suffire à elle seule. À part cela, je ne pense pas que le modèle de test puisse être beaucoup plus simple et tester encore ces différentes orientations UV.

Malgré les rotations UV, l'image de la carte normale est assez cohérente quant à la direction dans laquelle les vecteurs pointent, lorsque la carte normale est vue comme une image 2D. Dans une vue 2D de l'image, les côtés droits de tous les hémisphères ont des valeurs de canal rouge élevées et les bords supérieurs de tous les hémisphères ont des valeurs de canal vert élevées, quelles que soient les coordonnées UV. Cela suit la spécification glTF selon laquelle le rouge (+ X) pointe sur le côté droit de la texture dans l'espace UV et le vert (+ Y) pointe sur le bord supérieur de la texture dans l'espace UV. Il n'est pas nécessaire de fournir des vecteurs tangents pour faire ce travail: une simple estimation de l'espace écran de la direction générale de l'axe + U pour un triangle donné est suffisante pour calculer les vecteurs tangents et bi-tangents, comme le font BabylonJS et CesiumJS, et bien sûr ThreeJS fait aussi bien, mais avec des inversions d'axe normalScale encore inexpliquées.

Il y a un point de confusion connu avec l'utilisation par glTF de l'indicateur flipY , inversant les coordonnées V dans l'espace UV, ce que je pense que vous êtes déjà bien conscient. Je soupçonne depuis longtemps que cela joue un rôle dans le flip normalScale inexpliqué.

Il y a aussi une petite erreur de cuisson dans l'image (ma faute, j'étais un nouvel utilisateur de Substance Painter à l'époque, il y a des années). Une légère couture diagonale est visible notamment sur la partie plate des hémisphères dorés. Mais cela ne devrait pas nuire à l'effet global. La partie ronde principale de la cuisson s'est bien déroulée et démontre bien l'effet de «haut» et de «droite» constant sur toute l'image 2D de la texture normale, quelles que soient les coordonnées 3D ou UV pivotées dans toutes les directions et tangentes étant omis.

C'est un effet important pour les artistes qui créent des textures de carte normales répétées avant la création de la géométrie 3D, car l'artiste n'a pas besoin d'accéder aux coordonnées UV ou tangentes avant de créer l'image de texture.

@WestLangley J'aime ce test uniquement parce que c'est le meilleur que j'ai vu, c'est-à-dire le seul. Je serai ravi d'utiliser tout autre modèle de test que je désigne.

De plus, le tracé s'épaissit: le test original est un matériau double face. En raison de # 17804, j'ai décidé de l'essayer comme un seul côté. C'est toujours faux par défaut, mais maintenant j'obtiens la bonne réponse lorsque je règle normalScale.y = -1:
NormalTangentTestFrontNegY
À tout le moins, j'espère que nous pouvons convenir que le changement de double face ne devrait pas affecter le rendu de la face avant?

@emackey Un autre modèle de test qui pourrait être utile, je pense, serait un cube brillant avec des normales soudées et une carte normale correspondante conçue pour le rendre en forme de cube au lieu d'arrondi. Probablement deux versions, une avec et une sans tangentes fournies. Malheureusement, je n'ai pas les compétences en création pour le faire moi-même. Tout ce que je peux offrir, c'est un grand merci si vous le faites pour nous: priez:

@emackey Merci pour tout votre travail à ce sujet. Je l'apprécie beaucoup. Je comprends tous les problèmes qui peuvent survenir avec les artistes - et les problèmes qui doivent être surmontés.

Peut-être seriez-vous prêt à travailler avec @elalish et à fournir les modèles glb qu'il demande. J'espère que cela nous aiderait à comprendre.

@elalish Nous avons déjà abordé ces problèmes. À un moment donné, j'avais l'impression que tout fonctionnait. S'il y a un cas de test simple, je peux utiliser bisect pour identifier où les choses se sont cassées.

cube brillant avec des normales soudées et une carte normale correspondante conçue pour le rendre en forme de cube au lieu d'arrondi.

Terminé: normal_map_flat.zip . J'ai pris l'exemple des tangentes, écrit par l'équipe Draco, et j'ai créé une version supplémentaire sans l'attribut tangent.

À un moment donné, j'avais l'impression que tout fonctionnait.

Je pense que nous avons atteint un état où tout fonctionne _si_ le modèle comprend des tangentes. Sinon, les choses vont généralement bien, mais certains cas de bord sont possibles, en particulier autour des coutures UV. Dans ce cas, nous conseillons aux utilisateurs d'ajouter des tangentes. Ou, comme @emackey mentionné dans https://github.com/mrdoob/three.js/issues/11438#issuecomment -507027586, nous devrions peut-être restaurer le normalScale.y *= -1 flip dans GLTFLoader, uniquement dans les cas où le maillage omet les tangentes aux sommets.

@donmccurdy Merci. Je n'ai plus de temps pour ce problème aujourd'hui, mais j'ai encore du mal avec les côtés du cube plat. Si vous modifiez votre modèle pour avoir une surface brillante ( metallicFactor: 1, roughnessFactor: 0.1 ), vous verrez les reflets sur les côtés du cube nager un peu.

Je vois le même effet dans mon propre modèle de test. Pire encore, c'est un effet différent si je fais la cuisson normale dans Substance Painter par rapport à la cuisson dans Blender Cycles. Chaque programme le prépare pour qu'il soit correct pour sa propre fenêtre, mais il n'est pas parfaitement plat lorsqu'il est chargé dans l'autre programme, même sans glTF dans le mix. C'est très proche, mais sur une surface aussi plane, les plus petites différences en font un miroir funhouse, et pas dans le bon sens.

Je pense que je vois une différence subtile dans la façon dont les vecteurs normaux (ou espace TBN) sont interpolés entre les sommets à travers la face d'un triangle, dans SP vs Blender vs les différents moteurs WebGL. J'ai utilisé Gimp pour exécuter une «différence» entre une cuisson SP et une cuisson Blender, et elle est sortie en noir (valeurs RVB identiques) à chaque sommet, mais l'espace entre les sommets montre des différences tourbillonnantes d'intensité très faible.

Je m'attendrais à des effets fun-house subtils, mais pas à ce que j'obtiens actuellement (la version sans tangentes):
image
La version avec tangentes n'est pas vraiment meilleure, ce qui m'amène à me demander s'il s'agit vraiment d'un glTF valide:
image
@emackey Vous sentez-vous qualifié pour nous dire si l'un ou les deux modèles fournis par @donmccurdy sont en fait des glTF valides? Si c'est le cas, je pense que nous avons un problème majeur.

Ce cube particulier est un exemple si extrême que je ne sais pas vraiment quoi dire, ni si des réflexions physiquement réalistes sont une attente juste. Les textures normales sont destinées à ajouter des détails tels que des bosses et des bosses, et non à déformer toute la surface du maillage. C'était juste un moyen très efficace de tester que les tangentes sont lues ou générées correctement. Je sais que le changement de normalScale.y *= -1 n'a jamais été suffisant pour y remédier; c'était l'un des exemples qui nous a incités à prendre en charge les tangentes stockées en premier lieu.

Les réflexions sont également sauvages dans BabylonJS:
Screen Shot 2019-10-23 at 4 00 02 PM

Si le seul problème identifié est avec NormalTangentTest et qu'aucun utilisateur n'a proposé de modèles qui ne se rendent pas correctement dans la pratique, peut-être devrions-nous attendre de faire un changement ici?

Je suis toujours d'accord pour restaurer le multiplicateur normalScale.y *= -1 , mais le manque d'exemples du problème semble être une indication que le problème est mineur, plutôt qu'une indication que nous devons créer de tels exemples extrêmes.

Si le seul problème identifié est avec NormalTangentTest et qu'aucun utilisateur n'a proposé de modèles qui ne se rendent pas correctement dans la pratique, peut-être devrions-nous attendre de faire un changement ici?

Correction: https://github.com/mrdoob/three.js/issues/17804 suggère que quelque chose peut en effet avoir régressé avec des matériaux à double face. Cela semble valoir la peine d'être approfondi, même si pour plus de simplicité et de bon sens, nous devrions peut-être laisser le Cursed Cube seul pour le moment. 😇

Cette page vous a été utile?
0 / 5 - 0 notes