Three.js: Нормали перевернуты на некоторых частях сетки, у которых есть двусторонние материалы.

Созданный на 23 окт. 2019  ·  36Комментарии  ·  Источник: mrdoob/three.js

Описание проблемы

При обновлении со 107 до 108 я заметил, что части моей модели теперь выглядят так, как будто они перевернули нормали. Это происходит только с двусторонними материалами.
Смотрите здесь: https://vaaled-jonquil.glitch.me/

Вот как это должно выглядеть с отступом на обоих колесах. Так было в 107
Screen Shot 2019-10-23 at 3 51 35 PM

В 108, хотя одно из колес имеет узор, похожий на выскакивающий:
Screen Shot 2019-10-23 at 3 51 27 PM

Версия Three.js
  • [x] r108
Браузер
  • [x] Все они
Операционные системы
  • [x] Все они
  • [] Windows
  • [] macOS
  • [] Linux
  • [] Android
  • [] iOS
Bug Regression

Самый полезный комментарий

Исправляет это!
Screen Shot 2019-11-21 at 10 02 54 AM

Все 36 Комментарий

@pushmatrix Можете ли вы подтвердить, есть ли у этой модели собственные касательные? Мне любопытно, может ли это быть связано с # 11438.

Я не верю, что он указывает свои собственные касательные

Используя другой тестовый пример, git bisect указывает на # 17586 как PR, который инициировал проблему, если сетка двусторонняя и имеет отрицательный масштаб X. Однако это было в версии 109, а не в версии 108.

Это не было проверено на модели из этого PR, потому что эта модель недоступна.

Вот тестовый glb
wheels.glb.zip

3 года назад: https://github.com/mrdoob/three.js/issues/10331#issue -194807426

В основном кажется, что ... gl_FrontFace некорректно работает с двусторонними материалами на графических процессорах Adreno.

Это все еще проблема, которую мы должны решить?

//

Эта модель отображается правильно на моем компьютере, если мы удалим обходной путь Adreno в 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

Хм, я тоже помню, что читал этот комментарий; Думаю, я столкнулся с этим, глядя на эту ошибку, которая, по-видимому, была исправлена ​​в r108 (по крайней мере, обновление исправило ее для нас): https://github.com/GoogleWebComponents/model-viewer/issues/740

Может это связано? FWIW, я получил некоторые отзывы о старых графических процессорах Adreno, так что, по-видимому, они все еще довольно распространены.

Просто чтобы подтвердить, это все еще происходит в dev :

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

Просто чтобы подтвердить, это все еще происходит в dev:

Правильно. Но только не, если вы удалите обходной путь Adreno.

Но только не, если вы удалите обходной путь Adreno.

Как насчет того, чтобы временно удалить обходной путь, а затем искать другое решение? Текущая реализация приводит к неправильной визуализации независимо от того, какую платформу вы используете.

@ Mugen87 В этом конкретном случае я считаю, что геометрии являются зеркалами друг друга, но имеют одну и ту же карту нормалей.

Я согласен. Я потратил много часов, пытаясь найти обходной путь, совместимый с Adreno, который подходит для каждого случая использования.

При осмотре модели колеса каждое колесо имеет свою собственную УФ оболочку. Он не разделяет ни одного.
И порядок вершин противоположный.

Однако ориентация лица такая же, по крайней мере, в Blender.

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

Так что я не уверен, ошибка ли это в r108 или r108 теперь работает правильно. ¯_ (ツ) _ / ¯

@pushmatrix можно сравнить с Unity или Blender?

@mrdoob

Блендер 2.8

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

Единство

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

Sketchfab

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

@pushmatrix

Unity трудно увидеть, потому что свет идет в том же направлении, что и камера, но похоже, что Unity тоже ошибается? 🤔

@mrdoob

Да, это немного зависит от освещения, это в конечном итоге оказывается иллюзией и выглядит так, будто кое-где выскакивает. Но когда вы вращаетесь, он исчезает.

Вот это в другом свете, где это кажется правильным
Screen Shot 2019-11-20 at 2 36 16 PM

@mrdoob Лицевые стороны имеют порядок намотки против часовой стрелки в three.js. Обычно UV-координаты трех вершин каждой грани также имеют порядок поворота против часовой стрелки на UV-карте.

В этом примере моя _conjecture_ - модель слева имеет UV-развертки против часовой стрелки, а модель справа имеет UV-развертки _clockwise_. Модель справа отображается неправильно в three.js, когда doubleSide равно true .

AFAIK, удаление Adreno-обходного пути решает проблему для графических процессоров, отличных от Adreno.

@pushmatrix В вашем примере Unity есть направленные источники света с двух сторон. Это затрудняет понимание того, что происходит. Может быть предпочтительнее одиночный свет.

@WestLangley Я использую ту же карту среды, которую использует просмотрщик моделей , но, вероятно, у нее нет такого же поворота. Я попробую с одним светом.

@WestLangley Однонаправленный свет, вращающийся вокруг. Я чувствую, что смотрю на оптическую иллюзию

unity

Похоже, Unity заставляет канавки появляться и выходить в зависимости от того, как на них попадает свет, но, по крайней мере, это одинаково для обоих.

Threejs также согласован, но один определенно выглядит всегда выскакивающим:
wheels

Я чувствую, что с Unity что-то не так ...
Вроде нужно что-то вроде. material.normalMapScale.x = -1 .

@WestLangley

@mrdoob Лицевые стороны имеют порядок намотки против часовой стрелки в three.js. Обычно UV-координаты трех вершин каждой грани также имеют порядок поворота против часовой стрелки на UV-карте.

В этом примере моя _conjecture_ - модель слева имеет UV-развертки против часовой стрелки, а модель справа имеет UV-развертки _clockwise_. Модель справа отображается неправильно в three.js, когда doubleSide равно true .

AFAIK, удаление Adreno-обходного пути решает проблему для графических процессоров, отличных от Adreno.

Спасибо, в этом есть смысл. Я думаю, что это все еще заслуживает дальнейшего изучения. Если Sketchfab выглядит правильно на всех устройствах, нам, возможно, придется взглянуть на их шейдеры.

Сегодня я взглянул на соответствующий код шейдера, и мне кажется, что они не используют «Попиксельное отображение нормалей касательного пространства».

Согласно этому сообщению , Sketchfab ожидает определения касательных в активах или эти данные
генерировать на основе ультрафиолетовых координат. Это соответствует тому, что я видел в коде GLSL.

Если я добавлю касательные к модели с помощью BufferGeometryUtils.computeTangents() и установлю Material.vertexTangents равным true , результат будет выглядеть нормально:

image

Кстати: можно ли поделиться фрагментами кода из Sketchfab в этом посте ^^? В конце концов, это не открытый исходный код.

Для единообразия, вот Sketchfab с 1 направленным светом, движущимся вперед и назад:

sketchfab

Да, нужно иметь в виду, что Sketchfab выполняет обработку на своей стороне, когда вы загружаете модель, поэтому очень вероятно, что они могут создавать / изменять вещи. Вы просматриваете не файл glTF, а файл glTF, преобразованный в собственный формат.

@pushmatrix Не могли бы вы _подтвердить_, что # 17958 подходит для вашей модели?

three.js вычисляет касательные во фрагментном шейдере. Я считаю, что это работает правильно, если убрать хакерство для исправления некоторых глючных графических процессоров Adreno. См. № 17958.

//

Sketchfab вычисляет касательные на ЦП, когда касательные требуются и не предусмотрены моделью. ( @donmccurdy Это то, что требует спецификация glTF.)

three.js тоже может это сделать, но это будет означать добавление правильных касательных ко всем встроенным геометриям. three.js также должен будет реализовать алгоритм MIKKTSpace для замены ComputeTangents() . Поскольку three.js поддерживает как индексированные, так и неиндексированные геометрии, я ожидаю, что это потребует значительных усилий.

Исправляет это!
Screen Shot 2019-11-21 at 10 02 54 AM

Хорошо, похоже, что решение здесь состоит в том, чтобы отменить обходной путь Adreno (# 17958) и рекомендовать людям использовать BufferGeometryUtils.computeTangents() когда у модели нет касательных и они стремятся поддерживать графические процессоры Adreno (# 15850) .

Звучит неплохо?

Другое решение - автоматически вызвать BufferGeometryUtils.computeTangents() в движке, если он не указан, и удалить весь код, вычисляющий касательные во фрагментном шейдере ... 🤔

BufferGeometryUtils.computeTangents() настоящее время требует индекса, поэтому его нельзя использовать в ядре. Однако я думаю, что было бы предпочтительнее, если бы three.js в какой-то момент мог генерировать касательные согласно MIKKTSpace, а затем удалять perturbNormal2Arb() .

@WestLangley написал:

это будет означать добавление правильных касательных ко всем встроенным геометриям.

Передумал ... Касательные не обязательно. Вместо этого я бы восстановил метод BufferGeometry.computeTangents() и "переопределил" этот метод для всех встроенных геометрий, для которых мы можем задавать точные касательные аналитически.

//

Другое решение - автоматически вызвать BufferGeometryUtils.computeTangents () в движке, если он не указан, и удалить весь код, который вычисляет касательные во фрагментном шейдере ...

Мне это кажется правильным.

BufferGeometryUtils.computeTangents () в настоящее время требует индекса, поэтому его нельзя использовать в ядре.

Я думаю, это легко исправить. И его нужно будет исправить для поддержки https://github.com/mrdoob/three.js/issues/17804#issuecomment -557135610.

было бы предпочтительнее, если бы three.js мог генерировать касательные в соответствии с MIKKTSpace в какой-то момент, а затем удалять perturbNormal2Arb () ...

Я не знаю, должен ли MikkTSpace обязательно заменить вычисление фрагментного шейдера. Шейдер практически не требует затрат на производительность и отлично работает с большинством моделей. Предварительное вычисление касательных требует предоплаты за вершину каждый раз и не дает никаких преимуществ, за исключением этих особых случаев. 😕 Несмотря на то, что сказано в спецификации glTF, мне трудно оправдать это по умолчанию.

Было бы неплохо, если бы BufferGeometryUtils.computeTangents мог реализовать подход MikkTSpace - это лучший алгоритм, если вы должны предварительно вычислить их. Но, вероятно, было бы проще поместить MikkTSpace в такие инструменты, как glTF-Pipeline или gltfpack, которые могут использовать существующую собственную реализацию и выполнять расчет заранее, в автономном режиме.

@donmccurdy

Но, скажем ... в случае использования <model-viewer> , я не думаю, что существует надежный способ узнать, есть ли у пользователя графический процессор Adreno, но мы бы хотели выглядеть правильно (https: // github. ru / GoogleWebComponents / model-viewer / issues / 740).

Должен ли <model-viewer> (и другие проекты, которым нужна поддержка x-gpu) вызывать BufferGeometryUtils.computeTangents когда касательные не указаны, или следует использовать Three?

@pushmatrix

Ха-ха, я только что понял, откуда эти колеса 😁

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

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

@mrdoob 🕵

Хорошо, давайте пока вернемся к обходному пути.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги