Three.js: Le nouveau MeshStandardMaterial dans r112 a des artefacts de bandes sur certains GPU

Créé le 30 déc. 2019  ·  18Commentaires  ·  Source: mrdoob/three.js

À partir de r112, le MeshStandardMaterial par défaut (dans ce cas spécifiquement, car il est utilisé lors du chargement de fichiers glTF, mais probablement dans d'autres scénarios également) présente des artefacts de bandes sur certains GPU. Plus précisément, j'ai vu ce problème sur un Pixelbook et chaque génération de téléphones Pixel. Le problème ne se produit pas sur les GPU de bureau Nvidia que j'ai essayés, ni sur un Oculus Go ou Oculus Quest.

Il convient également de noter que j'ai observé que le nouveau shader du matériau a subi un impact notable sur les performances par rapport à r111 pour mon application particulière sur divers appareils mobiles, y compris ceux qui ne présentent pas les artefacts de rendu.

Les artefacts ressemblent à ceci (Rendu avec Three.js r112 sur un Pixelbook)

Screenshot 2019-12-29 at 9 15 19 PM

La sortie attendue ressemble à ceci (rendu avec Three.js r111 sur le même Pixelbook)

Screenshot 2019-12-29 at 9 24 31 PM

Lien en direct, utilisant actuellement r112: https://xrdinosaurs.com

Tous les dinosaures de cette page présentent le problème, mais ceux avec de grandes zones de couleurs plates ou lisses (comme le ventre du TRex) ont tendance à se démarquer davantage.

Le matériel de la capture d'écran (collé en entier ci-dessous) utilise l'extension glTF "KHR_materials_pbrSpecularGlossiness" et a une texture diffuse, normale et spéculaire / brillante.

    {
      "doubleSided": true,
      "emissiveFactor": [
        0,
        0,
        0
      ],
      "extensions": {
        "KHR_materials_pbrSpecularGlossiness": {
          "diffuseFactor": [
            0.46512957319999998,
            0.46512957319999998,
            0.46512957319999998,
            1
          ],
          "diffuseTexture": {
            "index": 4,
            "texCoord": 0
          },
          "glossinessFactor": 0.27610518290000002,
          "specularFactor": [
            0.92244664629999995,
            0.92244664629999995,
            0.92244664629999995
          ],
          "specularGlossinessTexture": {
            "index": 6,
            "texCoord": 0
          }
        }
      },
      "name": "TRex",
      "normalTexture": {
        "index": 5,
        "scale": 1,
        "texCoord": 0
      }
    }
Bug Device Issue

Commentaire le plus utile

@elalish après avoir exécuté des tests sur plusieurs exemples, j'ai pu identifier la cause réelle de ces artefacts. Contrairement à ce que j'ai dit dans mon article précédent, la cause première du problème n'est pas liée à un échantillonnage de coordonnées incorrectes, mais à la façon dont PMREMGenerator gère devicePixelRatio .

Sur les appareils avec virgule flottante nominale pixelRatio nous obtenons de "mauvaises" coordonnées mipmap sur la texture générée. Il y a donc de petits écarts et des régions qui se chevauchent. J'ai également découvert que ma machine avait des devicePixelRatios selon que je visualisais l'exemple localement (0.899 ..) ou en ligne (1) et c'est pourquoi je ne connaissais ces artefacts que localement.

J'ai configuré un test simple en désactivant setPixelRatio et cela semble résoudre le problème, si c'est le cas pour d'autres, alors il serait préférable de refactoriser la façon dont PMREMGenerator gère.

@toji @ plut0nist l' esprit de vérifier les exemples suivants sur les appareils qui ont présenté ces artefacts?

Exemple DEV
Exemple TEST

Tous les 18 commentaires

Je peux confirmer le problème sur mon Pixel (1). Cependant, les artefacts ne semblent se produire que lorsque KHR_materials_pbrSpecularGlossiness est utilisé.

18042 a introduit l'anti-crénelage géométrique. Cependant, GLTFLoader remplace le code du bloc de shader lights_physical_fragment par ce qui suit:

https://github.com/mrdoob/three.js/blob/3ba0553208cfc9113152f5f39b4036a448cf3f25/examples/js/loaders/GLTFLoader.js#L683 -L688

@toji Pouvez-vous modifier la copie GLTFLoader de votre application en remplaçant le code ci-dessus par:

var lightPhysicalFragmentChunk = [
    'PhysicalMaterial material;',
    'material.diffuseColor = diffuseColor.rgb;',
    'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );',
    'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );',

    'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 );// 0.0525 corresponds to the base mip of a 256 cubemap.',
    'material.specularRoughness += geometryRoughness;',
    'material.specularRoughness = min( material.specularRoughness, 1.0 );',
    'material.specularColor = specularFactor.rgb;',
].join( '\n' );

@elalish Même si ce patch ne résout pas le problème, nous devrions l'ajouter à GLTFLoader dans tous les cas, non?

@ Mugen87 Oui, bien sûr. Je vais voir si je peux également reproduire sur mon Pixel 3.

@toji je ne repro sur mon Pixel 3 avec Android 10, donc au moins ce ne sont pas

Bug du pilote? Je ne sais pas quelle ligne le déclenche. La présence / l'absence d'anti-aliasing géométrique ne ferait pas de toute façon ce type d'artefact, j'en suis presque sûr.

@toji Je viens également d'essayer mon téléphone testeur de merde dédié (un Alcatel à environ 30 $ de Target). Je ne reçois pas les artefacts de baguage, même si j'ai remarqué que le ciel est noir. Quant à la perf: ce n'est pas exactement beurré, mais ce n'est pas mal. Honnêtement, cette vue 3D répond mieux que l'interface utilisateur du clavier 2D sur ce téléphone. Allez comprendre.

Ugh, le ciel noir signifie en fait que la génération PMREM a complètement échoué, et c'est à cause de ce bug étrange et de cette solution de contournement que nous avons trouvée: https://github.com/GoogleWebComponents/model-viewer/pull/920. J'avais l'intention de mettre ça dans r112, mais ça m'a échappé. Cependant, probablement sans rapport avec le bogue de l'OP.

J'ai reproduit l'erreur d'OP sur mon OnePlus 5T dans Chrome et Firefox.

Merci! Appliqué le correctif à GLTFLoader et observé que l'artefact de bandes était réduit, mais pas éliminé.

Avant le patch:

Screenshot 2019-12-30 at 12 11 36 PM

Après correctif:

Screenshot 2019-12-30 at 12 10 35 PM

En gros, on dirait que cela a corrigé l'un des groupes. :) (Captures d'écran prises sur un Pixelbook, mais également observées sur un Pixel 4 XL. Je n'ai pas d'autres appareils de test sur moi pour le moment.)

Heureux de patcher tous les autres changements que vous souhaitez tester, ou vous pouvez consulter https://github.com/toji/xr-dinosaurs si vous souhaitez le tester localement. (Ne nécessite aucun composant serveur.)

Quant à la perf: ce n'est pas exactement beurré, mais ce n'est pas mal.

Je vous remercie. 😁 J'ai travaillé dur pour la performance. Dans ce cas, lors de la mise à jour vers r112, j'ai remarqué la régression perf et j'ai demandé à MeshLambertMaterial pour vérifier. Étant donné que le modèle d'environnement n'avait de toute façon pas correctement configuré les matériaux PBR, je le force à Lambert et je l'ai laissé là, ce qui a récupéré toutes les régressions de performance, puis certaines (car il occupe une grande partie de la fenêtre.) Laissé les dinosaures avec le matériel standard puisqu'ils sont l'atout «héros».

Remarque: Scene.environment ne fonctionne actuellement pas lorsque GLTFLoader charge des maillages en utilisant des matériaux spéculaires / brillants puisque le moteur de rendu vérifie MeshStandardMaterial eg

https://github.com/mrdoob/three.js/blob/dd81aad5513d66e35e51f3a3b42555bc8aef5cbc/src/renderers/WebGLRenderer.js#L1633

Cependant, GLTFLoader crée un ShaderMaterial pour chaque matériau spéculaire / brillant. Je m'en suis rendu compte aujourd'hui lors des tests avec le modèle T-Rex ^^. Eh bien, une raison de plus pour finalement terminer # 14099.

Je ne peux pas reproduire les artefacts de bandes lorsqu'aucune carte d'environnement n'est utilisée. Tout a l'air bien sur un Pixel (1) avec une configuration d'éclairage simple comme une lumière ambiante et une lumière ponctuelle.

Hum, il semble y avoir plus de problèmes avec les matériaux physiques en utilisant une carte d'environnement. Découvrez à quoi ressemblent les exemples suivants sur un Pixel 1, Android 10.

https://threejs.org/examples/webgl_materials_envmaps_exr

Screenshot_20200106-111923

https://threejs.org/examples/webgl_materials_physical_clearcoat

Screenshot_20200106-111856

https://threejs.org/examples/webgl_materials_envmaps_hdr

Screenshot_20200106-111809

Je ne peux pas reproduire ces artefacts sur mon iMac.

Quelque chose d'assez bizarre se passe sur ma machine, je suis capable de reproduire ces artefacts sur tous les exemples qui utilisent le nouveau PMREMGenerator . Mais seulement lors de leur exécution localement; lors de leur visualisation en ligne (à partir de la page threejs.org), ils semblent tous corrects.

Je n'ai jamais rencontré un tel problème et je suis complètement perdu quant à sa cause.

J'exécute un Windows 10 avec une GeForce GTX 750 TI, donc je ne pense pas que ce soit un problème spécifique à l'appareil.

Mais seulement lors de leur exécution localement; lors de leur visualisation en ligne (à partir de la page threejs.org), ils semblent tous corrects.

Sachez que la version dev a des changements qui ne sont pas encore présents dans prod . Pouvez-vous s'il vous plaît vérifier la branche actuelle master puis effectuer un test local?

Pouvez-vous s'il vous plaît vérifier la branche principale actuelle, puis effectuer un test local?

Première chose que j'ai essayée après avoir remarqué ce comportement, les mêmes résultats. Vraiment curieux.

Pouvez-vous s'il vous plaît tester avec des fenêtres de navigation privée? Parfois, des éléments peuvent être mis en cache ou des extensions de navigateur interfèrent, ce qui peut être vraiment déroutant

Essayé avec incognito, mêmes résultats :(

Je crois que je sais pourquoi ce baguage se produit.
Pendant la génération de mip, ma version locale (incorrecte) crée une bande noire qui n'existe pas sur la version live.

local
online

Ceci est également reproduit à des niveaux mip inférieurs.

Cela se produit probablement en raison d'un échantillonnage de coordonnées incorrect, j'ai expérimenté la désactivation manuelle du filtrage anisotrope, mais le problème persiste. Il est difficile de déterminer exactement ce qui se passe, surtout parce que je ne peux pas comprendre pourquoi cela présente deux comportements différents sur la même machine.

@elalish après avoir exécuté des tests sur plusieurs exemples, j'ai pu identifier la cause réelle de ces artefacts. Contrairement à ce que j'ai dit dans mon article précédent, la cause première du problème n'est pas liée à un échantillonnage de coordonnées incorrectes, mais à la façon dont PMREMGenerator gère devicePixelRatio .

Sur les appareils avec virgule flottante nominale pixelRatio nous obtenons de "mauvaises" coordonnées mipmap sur la texture générée. Il y a donc de petits écarts et des régions qui se chevauchent. J'ai également découvert que ma machine avait des devicePixelRatios selon que je visualisais l'exemple localement (0.899 ..) ou en ligne (1) et c'est pourquoi je ne connaissais ces artefacts que localement.

J'ai configuré un test simple en désactivant setPixelRatio et cela semble résoudre le problème, si c'est le cas pour d'autres, alors il serait préférable de refactoriser la façon dont PMREMGenerator gère.

@toji @ plut0nist l' esprit de vérifier les exemples suivants sur les appareils qui ont présenté ces artefacts?

Exemple DEV
Exemple TEST

@sciecode Merci beaucoup d'avoir découvert la cause profonde! Je vais voir si je peux trouver une solution.

@sciecode : vérifié sur mes appareils. Le test d'exemple DEV présente une ligne noire très évidente dans la carte d'environnement, contrairement à l'exemple TEST. Débogage génial, merci!

J'étais extrêmement curieux de savoir pourquoi PMREMGenerator avait besoin de rendre compte de devicePixelRatio , alors je suis allé creuser. Il s'avère que c'est parce que renderer.setViewport() factorise automatiquement le DPR dans toutes les valeurs que vous lui envoyez, ce qui donne l'impression que c'est clairement le mauvais comportement pour les cibles de rendu autres que le framebuffer par défaut, qui sont allouées avec des valeurs de pixels exactes qui ne le sont pas. tenir compte du rapport de pixels du tout. Je suggérerais que le "bon" comportement ici est qu'en interne rendere.setViewport() devrait utiliser getTargetPixelRatio() , ce qui renvoie 1 lorsque la cible de rendu active est autre que la valeur par défaut. Je peux certainement voir comment cela pourrait introduire des problèmes de compatibilité descendante.

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