Three.js: Normals invertidos em algumas partes da malha que possuem materiais de dupla face

Criado em 23 out. 2019  ·  36Comentários  ·  Fonte: mrdoob/three.js

Descrição do problema

Atualizando de 107 para 108, notei que partes do meu modelo agora parecem ter os normais invertidos. Isso ocorre apenas com materiais de dupla face.
Veja aqui: https://vaulted-jonquil.glitch.me/

É assim que deve ser, com o padrão sendo recortado em ambas as rodas. Era assim em 107
Screen Shot 2019-10-23 at 3 51 35 PM

No 108, embora uma das rodas tenha o padrão parecendo que está saindo:
Screen Shot 2019-10-23 at 3 51 27 PM

Versão Three.js
  • [x] r108
Navegador
  • [x] Todos eles
SO
  • [x] Todos eles
  • [ ] Janelas
  • [ ] Mac OS
  • [] Linux
  • [] Android
  • [] iOS
Bug Regression

Comentários muito úteis

Corrige isso!
Screen Shot 2019-11-21 at 10 02 54 AM

Todos 36 comentários

@pushmatrix Você pode confirmar se este modelo fornece ou não suas próprias tangentes? Estou curioso para saber se isso pode estar relacionado a # 11438.

Eu não acredito que especifique suas próprias tangentes

Usando um caso de teste diferente, git bissect aponta para # 17586 como o PR que iniciou o problema se a malha for dupla-face e tiver escala X negativa. Isso estava em r.109, não em r.108, no entanto.

Isso não foi testado no modelo deste PR porque esse modelo não está disponível.

Aqui está o glb de teste
wheel.glb.zip

De 3 anos atrás: https://github.com/mrdoob/three.js/issues/10331#issue -194807426

Basicamente, parece que ... gl_FrontFace não está funcionando corretamente com materiais de dupla face nas GPUs Adreno.

Este _ainda_ é um problema que precisamos resolver?

//

Este modelo parece ser renderizado corretamente em minha máquina se removermos a solução alternativa Adreno em normalmap_pars_fragment :

#ifdef DOUBLE_SIDED

    // Workaround for Adreno GPUs gl_FrontFacing bug. See #15850 and #10331

    bool frontFacing = dot( cross( S, T ), N ) > 0.0;

    mapN.xy *= ( float( frontFacing ) * 2.0 - 1.0 );

#else

    mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );

#endif

Hmm, também me lembro de ter lido esse comentário; Acho que me deparei com isso enquanto olhava para esse bug, que aparentemente foi corrigido no r108 (pelo menos a atualização corrigiu para nós): https://github.com/GoogleWebComponents/model-viewer/issues/740

Talvez esteja relacionado? FWIW, recebi alguns comentários sobre GPUs Adreno antigas, então, aparentemente, eles ainda são bastante comuns.

Só para confirmar, isso ainda acontece em dev :

Screen Shot 2019-10-25 at 4 06 51 PM

Só para confirmar, isso ainda acontece no dev:

Direito. Mas não se você remover a solução alternativa para o bug Adreno.

Mas não se você remover a solução alternativa para o bug Adreno.

Que tal remover a solução alternativa de vez em quando procurar uma solução diferente? A implementação atual leva a imagens erradas, independentemente da plataforma que você está usando.

@ Mugen87 Neste caso particular, acredito que as geometrias são espelhos uma da outra, mas compartilham o mesmo mapa normal.

Eu concordo com você. Eu investi muitas horas tentando encontrar uma solução alternativa compatível com Adreno que seja correta em todos os casos de uso.

Inspecionando o modelo do modelo da roda, cada roda tem seu próprio escudo UV. Ele não compartilha um.
E a ordem do vértice é oposta.

Porém a orientação da face é a mesma, pelo menos no Blender.

Screen Shot 2019-11-18 at 11 21 05 AM

Portanto, não tenho certeza se é um bug no r108 ou se o r108 agora está fazendo as coisas corretamente. ¯_ (ツ) _ / ¯

@pushmatrix você pode comparar com Unity ou Blender?

@mrdoob

Blender 2.8

Screen Shot 2019-11-20 at 8 27 27 AM

Unidade

Screen Shot 2019-11-20 at 8 40 30 AM

Sketchfab

Screen Shot 2019-11-20 at 8 32 48 AM

@pushmatrix

O Unity é difícil de ver porque a luz está na mesma direção da câmera, mas parece que o Unity também está errado? 🤔

@mrdoob

Sim, depende um pouco da iluminação, acaba sendo uma ilusão e parece que apareceu em alguns lugares. Mas quando você gira, ele desaparece.

Aqui está em outra luz, onde parece correto
Screen Shot 2019-11-20 at 2 36 16 PM

@mrdoob As faces frontais têm ordem de enrolamento no sentido anti-horário em three.js. Normalmente, os UVs dos 3 vértices de cada face também têm ordem de enrolamento no sentido anti-horário no mapa de UV.

Neste exemplo, minha _conjectura_ é o modelo à esquerda tem UVs anti-horário e o modelo à direita tem UVs _clockwise_. O modelo à direita está renderizando incorretamente em three.js quando doubleSide é true .

AFAIK, a remoção da solução alternativa Adreno corrige o problema para GPUs não Adreno.

@pushmatrix Seu exemplo do Unity parece ter luzes direcionais de duas direções. Isso torna difícil discernir o que está acontecendo. Uma única luz pode ser preferível.

@WestLangley Estou usando o mesmo mapa de ambiente que o visualizador de modelo está usando, mas provavelmente não tem a mesma rotação. Vou tentar com uma única luz.

@WestLangley Luz direcional única girando. Eu sinto que estou olhando para uma ilusão de ótica

unity

Parece que o Unity faz os sulcos aparecerem e desaparecerem dependendo de como a luz o atinge, mas pelo menos é consistente para ambos.

Threejs também é consistente, mas um definitivamente parece sempre estourado:
wheels

Eu sinto que há algo errado com o Unity ...
Como se precisasse de algo parecido. material.normalMapScale.x = -1 .

@WestLangley

@mrdoob As faces frontais têm ordem de enrolamento no sentido anti-horário em three.js. Normalmente, os UVs dos 3 vértices de cada face também têm ordem de enrolamento no sentido anti-horário no mapa de UV.

Neste exemplo, minha _conjectura_ é o modelo à esquerda tem UVs anti-horário e o modelo à direita tem UVs _clockwise_. O modelo à direita está renderizando incorretamente em three.js quando doubleSide é true .

AFAIK, a remoção da solução alternativa Adreno corrige o problema para GPUs não Adreno.

Obrigado, isso faz sentido. Eu acho que ainda merece mais investigação. Se Sketchfab parece correto em todos os dispositivos, podemos ter que olhar seus shaders.

Eu dei uma olhada no respectivo código de sombreador hoje e parece que eles não estão usando o "Mapeamento normal do espaço tangente por pixel".

De acordo com esta postagem , Sketchfab espera definições tangentes em ativos ou esses dados são
gerar com base em coordenadas uv. Isso corresponde ao que vi no código GLSL.

Se eu adicionar tangentes ao modelo via BufferGeometryUtils.computeTangents() e definir Material.vertexTangents como true , o resultado parece bom:

image

BTW: Há algum problema em compartilhar trechos de código do Sketchfab neste post ^^? Afinal, não é um código aberto.

Para consistência, aqui está o Sketchfab com 1 luz direcional se movendo para frente e para trás:

sketchfab

Sim, uma coisa a ter em mente é que o Sketchfab faz o processamento no final deles quando você carrega um modelo, então muito provavelmente eles podem estar gerando / alterando coisas. O que você está vendo não é um glTF, mas um arquivo glTF convertido em seu próprio formato.

@pushmatrix Você pode _por favor_ demonstrar que # 17958 funciona para o seu modelo?

three.js calcula tangentes no sombreador de fragmento. Acredito que funcione corretamente se um hack para acomodar certas GPUs Adreno com erros for removido. Veja # 17958.

//

Sketchfab calcula tangentes na CPU quando tangentes são necessárias e não são fornecidas pelo modelo. ( @donmccurdy Isso é o que a especificação glTF requer.)

three.js pode fazer isso também, mas significará adicionar tangentes corretas a todas as geometrias integradas. three.js também terá que implementar o algoritmo MIKKTSpace para substituir ComputeTangents() . Como three.js oferece suporte a geometrias indexadas e não indexadas, espero que isso exija um esforço considerável.

Corrige isso!
Screen Shot 2019-11-21 at 10 02 54 AM

Ok, então parece que a solução aqui é reverter a solução alternativa do Adreno (# 17958) e recomendar às pessoas que usem BufferGeometryUtils.computeTangents() quando o modelo não tiver tangentes e o objetivo for dar suporte às GPUs Adreno (# 15850) .

Parece bom?

Outra solução é chamar BufferGeometryUtils.computeTangents() no motor automaticamente quando não for fornecido e remover todo o código que calcula tangentes no sombreador de fragmento ... 🤔

BufferGeometryUtils.computeTangents() atualmente requer um índice, portanto não pode ser usado no núcleo. No entanto, acho que seria preferível se three.js pudesse gerar tangentes de acordo com o MIKKTSpace em algum ponto e depois remover perturbNormal2Arb() .

@WestLangley escreveu:

significará adicionar tangentes corretas a todas as geometrias integradas.

Mudando de ideia ... Tangentes não são necessariamente necessárias. Em vez disso, eu restauraria o método BufferGeometry.computeTangents() e "substituiria" esse método para todas as geometrias integradas para as quais podemos definir tangentes exatas analiticamente.

//

Outra solução é chamar BufferGeometryUtils.computeTangents () no mecanismo automaticamente quando não for fornecido e remover todo o código que calcula tangentes no sombreador de fragmento ...

Isso parece certo para mim.

BufferGeometryUtils.computeTangents () atualmente requer um índice, portanto, não pode ser usado no núcleo.

Eu acho que isso é facilmente corrigido. E terá que ser consertado para oferecer suporte a https://github.com/mrdoob/three.js/issues/17804#issuecomment -557135610.

seria preferível se three.js pudesse gerar tangentes de acordo com MIKKTSpace em algum ponto e, em seguida, remover perturbNormal2Arb () ...

Não sei se o MikkTSpace deve necessariamente substituir o cálculo do sombreador de fragmento. O shader quase não tem custo de desempenho e funciona bem para a maioria dos modelos. A pré-computação de tangentes sempre tem um custo inicial por vértice e nenhum benefício, exceto nesses casos específicos. 😕 Apesar do que a especificação glTF diz, estou tendo dificuldade em justificar fazer isso por padrão.

Seria bom se BufferGeometryUtils.computeTangents pudesse implementar a abordagem MikkTSpace - esse é o melhor algoritmo, se você deve pré-calculá-los. Mas provavelmente seria mais fácil colocar o MikkTSpace em ferramentas como glTF-Pipeline ou gltfpack, que podem usar a implementação nativa existente e fazer o cálculo com antecedência, offline.

@donmccurdy

Mas, digamos ... no caso de uso <model-viewer> , não acho que haja uma maneira confiável de saber se o usuário tem uma GPU Adreno, mas gostaríamos de olhar certo (https: // github. com / GoogleWebComponents / model-viewer / issues / 740).

<model-viewer> (e outros projetos que desejam suporte x-gpu) devem chamar BufferGeometryUtils.computeTangents quando as tangentes não são fornecidas, ou três?

@pushmatrix

Haha, acabei de perceber de onde vêm essas rodas 😁

https://bumbleride.com/products/era?variant=20719969599599

Screen Shot 2019-11-22 at 3 52 19 PM

@mrdoob 🕵

Ok, então, vamos reverter a solução alternativa por enquanto.

Esta página foi útil?
0 / 5 - 0 avaliações