Three.js: Normales volteados en algunas partes de la malla que tienen materiales de doble cara

Creado en 23 oct. 2019  ·  36Comentarios  ·  Fuente: mrdoob/three.js

Descripción del problema

Al actualizar de 107 a 108, he notado que algunas partes de mi modelo ahora parecen tener valores normales invertidos. Esto solo ocurre con materiales de doble cara.
Ver aquí: https://vaulted-jonquil.glitch.me/

Así es como debería verse, con el patrón con sangría en ambas ruedas. Así fue en 107
Screen Shot 2019-10-23 at 3 51 35 PM

En 108, aunque una de las ruedas tiene el patrón que parece estar saliendo:
Screen Shot 2019-10-23 at 3 51 27 PM

Versión Three.js
  • [x] r108
Navegador
  • [x] Todos ellos
SO
  • [x] Todos ellos
  • [] Windows
  • [ ] Mac OS
  • [] Linux
  • [] Android
  • [] iOS
Bug Regression

Comentario más útil

¡Lo arregla!
Screen Shot 2019-11-21 at 10 02 54 AM

Todos 36 comentarios

@pushmatrix ¿Puede confirmar si este modelo proporciona o no sus propias tangentes? Tengo curiosidad por saber si esto puede estar relacionado con el # 11438.

No creo que especifique sus propias tangentes

Usando un caso de prueba diferente, git bisect apunta a # 17586 como el PR que inició el problema si la malla es de doble cara y tiene una escala negativa X. Sin embargo, eso estaba en r.109, no en r.108.

Esto no se probó en el modelo de este PR porque ese modelo no está disponible.

Aquí está la prueba glb
wheels.glb.zip

Desde hace 3 años: https://github.com/mrdoob/three.js/issues/10331#issue -194807426

Básicamente, parece que ... gl_FrontFace no funciona correctamente con materiales de doble cara en las GPU Adreno.

¿Es este _todavía_ un problema que debemos resolver?

//

Este modelo parece renderizarse correctamente en mi máquina si eliminamos la solución alternativa de Adreno en 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, recuerdo haber leído ese comentario también; Creo que lo encontré mientras miraba este error, que aparentemente se solucionó en r108 (al menos la actualización lo solucionó para nosotros): https://github.com/GoogleWebComponents/model-viewer/issues/740

¿Quizás esté relacionado? FWIW, he recibido algunos comentarios sobre las antiguas GPU Adreno, por lo que aparentemente todavía son bastante comunes.

Solo para confirmar, esto todavía sucede en dev :

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

Solo para confirmar, esto todavía sucede en dev:

Correcto. Pero no si elimina la solución alternativa del error Adreno.

Pero no si elimina la solución alternativa del error Adreno.

¿Qué tal eliminar la solución temporal por ahora y luego buscar una solución diferente? La implementación actual conduce a imágenes incorrectas sin importar qué plataforma esté utilizando.

@ Mugen87 En este caso particular, creo que las geometrías son espejos entre sí, pero comparten el mismo mapa normal.

Estoy de acuerdo contigo. He invertido muchas horas tratando de encontrar una solución alternativa compatible con Adreno que sea correcta en cada caso de uso.

Al inspeccionar el modelo de modelo de rueda, cada rueda tiene su propia carcasa UV. No comparte uno.
Y el orden de los vértices es opuesto.

Sin embargo, la orientación de la cara es la misma, al menos en Blender.

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

Así que no estoy seguro de si es un error en r108, ​​o si r108 ahora está haciendo las cosas correctamente. ¯_ (ツ) _ / ¯

@pushmatrix se puede comparar con Unity o Blender?

@mrdoob

Licuadora 2.8

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

Unidad

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

Sketchfab

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

@pushmatrix

Unity es difícil de ver porque la luz está en la misma dirección que la cámara, pero ¿parece que Unity también está mal? 🤔

@mrdoob

Sí, depende un poco de la iluminación, termina siendo una ilusión y parece que apareció en algunos lugares. Pero cuando giras, desaparece.

Aquí está bajo otra luz, donde parece correcto
Screen Shot 2019-11-20 at 2 36 16 PM

@mrdoob Las caras frontales tienen un orden de enrollamiento en sentido antihorario en three.js. Normalmente, los rayos ultravioleta de los 3 vértices de cada cara también tienen un orden de enrollamiento en sentido antihorario en el mapa de rayos ultravioleta.

En este ejemplo, mi _conjetura_ es que el modelo de la izquierda tiene UV en sentido antihorario y el modelo de la derecha tiene UV _ en sentido horario_. El modelo de la derecha se muestra incorrectamente en three.js cuando doubleSide es true .

AFAIK, la eliminación de la solución alternativa de Adreno soluciona el problema de las GPU que no son de Adreno.

@pushmatrix Su ejemplo de Unity parece tener luces direccionales desde dos direcciones. Eso hace que sea difícil discernir lo que está sucediendo. Puede ser preferible una sola luz.

@WestLangley Estoy usando el mismo mapa de entorno que usa el visor de modelos , pero es probable que no tenga la misma rotación. Lo intentaré con una sola luz.

@WestLangley Luz direccional única que gira alrededor. Siento que estoy mirando una ilusión óptica

unity

Parece que Unity hace que los surcos entren y salgan dependiendo de cómo la luz lo golpee, pero al menos es consistente para ambos.

Threejs también es consistente, pero uno definitivamente parece siempre salido:
wheels

Siento que hay algo mal en el de Unity ...
Como si necesitara algo como. material.normalMapScale.x = -1 .

@WestLangley

@mrdoob Las caras frontales tienen un orden de enrollamiento en sentido antihorario en three.js. Normalmente, los rayos ultravioleta de los 3 vértices de cada cara también tienen un orden de enrollamiento en sentido antihorario en el mapa de rayos ultravioleta.

En este ejemplo, mi _conjetura_ es que el modelo de la izquierda tiene UV en sentido antihorario y el modelo de la derecha tiene UV _ en sentido horario_. El modelo de la derecha se muestra incorrectamente en three.js cuando doubleSide es true .

AFAIK, la eliminación de la solución alternativa de Adreno soluciona el problema de las GPU que no son de Adreno.

Gracias, eso tiene sentido. Sin embargo, creo que aún merece más investigación. Si Sketchfab se ve correcto en todos los dispositivos, es posible que tengamos que mirar sus sombreadores.

Hoy eché un vistazo al código de sombreado respectivo y parece que no están usando "Asignación normal de espacio tangente por píxel".

Según esta publicación , Sketchfab espera definiciones tangentes en los activos o estos datos son
generar basado en coordenadas uv. Eso corresponde a lo que he visto en el código GLSL.

Si agrego tangentes al modelo a través de BufferGeometryUtils.computeTangents() y establezco Material.vertexTangents en true , el resultado parece correcto:

image

Por cierto: ¿Está bien compartir fragmentos de código de Sketchfab en esta publicación ^^? Después de todo, no es de código abierto.

Para mayor coherencia, aquí está Sketchfab con 1 luz direccional moviéndose hacia adelante y hacia atrás:

sketchfab

Sí, una cosa a tener en cuenta es que Sketchfab procesa en su extremo cuando subes un modelo, por lo que es muy probable que estén generando o cambiando cosas. Lo que está viendo no es un glTF sino un archivo glTF convertido a su propio formato.

@pushmatrix ¿Puede

three.js calcula tangentes en el sombreador de fragmentos. Creo que funciona correctamente si se elimina un truco para acomodar ciertas GPU Adreno con errores. Ver # 17958.

//

Sketchfab calcula tangentes en la CPU cuando se requieren tangentes y no las proporciona el modelo. ( @donmccurdy Esto es lo que requiere la especificación glTF).

three.js también puede hacer eso, pero significará agregar tangentes correctas a todas las geometrías integradas. three.js también tendrá que implementar el algoritmo MIKKTSpace para reemplazar ComputeTangents() . Dado que three.js admite geometrías indexadas y no indexadas, espero que esto requiera un esfuerzo considerable.

¡Lo arregla!
Screen Shot 2019-11-21 at 10 02 54 AM

Bien, parece que la solución aquí es revertir la solución alternativa de Adreno (# 17958) y recomendar a las personas que usen BufferGeometryUtils.computeTangents() cuando el modelo no tiene tangentes y su objetivo es admitir las GPU de Adreno (# 15850) .

¿Suena bien?

Otra solución es llamar BufferGeometryUtils.computeTangents() en el motor automáticamente cuando no se proporciona y eliminar todo el código que calcula tangentes en el sombreador de fragmentos ... 🤔

BufferGeometryUtils.computeTangents() actualmente requiere un índice, por lo que no se puede usar en el núcleo. Sin embargo, creo que sería preferible si three.js pudiera generar tangentes según MIKKTSpace en algún momento y luego eliminar perturbNormal2Arb() .

@WestLangley escribió:

significará agregar tangentes correctas a todas las geometrías integradas.

Cambiando de opinión ... Las tangentes no son necesariamente necesarias. En su lugar, restauraría el método BufferGeometry.computeTangents() y "anularía" ese método para todas las geometrías integradas para las que podemos establecer tangentes exactas analíticamente.

//

Otra solución es llamar a BufferGeometryUtils.computeTangents () en el motor automáticamente cuando no se proporciona y eliminar todo el código que calcula tangentes en el sombreador de fragmentos ...

Suena bien para mi.

BufferGeometryUtils.computeTangents () actualmente requiere un índice, por lo que no se puede usar en el núcleo.

Creo que se soluciona fácilmente. Y tendrá que arreglarse para admitir https://github.com/mrdoob/three.js/issues/17804#issuecomment -557135610.

sería preferible si three.js pudiera generar tangentes de acuerdo con MIKKTSpace en algún momento y luego eliminar perturbNormal2Arb () ...

No sé si MikkTSpace debería reemplazar necesariamente el cálculo del sombreador de fragmentos. El sombreador casi no tiene costo de rendimiento y funciona bien para la mayoría de modelos. La precomputación de tangentes tiene un costo inicial por vértice cada vez, y ningún beneficio excepto en estos casos específicos. 😕 A pesar de lo que dice la especificación glTF, me cuesta mucho justificar hacerlo de forma predeterminada.

Sería bueno si BufferGeometryUtils.computeTangents pudiera implementar el enfoque MikkTSpace; ese es el mejor algoritmo, si debe calcularlos previamente. Pero probablemente sería más fácil poner MikkTSpace en herramientas como glTF-Pipeline o gltfpack, que pueden usar la implementación nativa existente y hacer el cálculo por adelantado, sin conexión.

@donmccurdy

Pero, digamos ... en el caso de uso <model-viewer> , no creo que haya una forma confiable de saber si el usuario tiene una GPU Adreno, pero nos gustaría ver bien (https: // github. com / GoogleWebComponents / model-viewer / issues / 740).

¿Debería <model-viewer> (y otros proyectos que quieran compatibilidad con x-gpu) llamar a BufferGeometryUtils.computeTangents cuando no se proporcionan tangentes, o debería Tres?

@pushmatrix

Jaja, me acabo de dar cuenta de dónde son estas ruedas 😁

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

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

@mrdoob 🕵

Bien, revertiremos la solución por ahora.

¿Fue útil esta página
0 / 5 - 0 calificaciones