Общий Обзор Нанита

После показа впечатляющей демоверсии год назад и недавнего включения в превью UE5, Nanite теперь стала очень популярной темой.

Мне просто хотелось окунуться в эту тему и немного развлечься, пытаясь разобраться и объяснить, как, на мой взгляд, все это работает и какие технические решения за этим стоят, используя кадр, запечатленный в РендерДок .

Стоит отдать должное Epic за то, что она сохраняет свою технологию открытой, что облегчает ее изучение и структурирование; В редакторе есть маркеры и отладочная информация, которые оказываются очень полезными.

Это кадры, которые мы собираемся посмотреть из демо-проекта «Долина Древних».

Он показывает взаимодействие между нанитовой и ненанитовой геометрией, и это сделано действительно круто.



Общий обзор Нанита



Нанит::CullRasterize

Первым шагом в этом процессе является Nanite::CullRasterize, и он выглядит следующим образом.

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

Ниже показано, как это выглядит на отснятом кадре.



Общий обзор Нанита



Фильтрация экземпляров объектов

Первое, что здесь происходит, — это устранение экземпляров объектов.

Похоже, что это фильтрация по пирамиде видимости и окклюзии на графическом процессоре.

Здесь используются как данные экземпляра, так и данные примитива, и я предполагаю, что сначала отфильтровываются целые объекты, а если экземпляр остается, то фильтрация начинается на более детальном уровне.

Буфер Nanite.Views хранит информацию о камере для пирамидальной фильтрации и иерархический буфер глубины (HZB), используемый для фильтрации окклюзии.

Для построения иерархического буфера глубины данные берутся из предыдущего кадра и перепроецируются на текущий.

Я не уверен, как обрабатываются динамические объекты.

Возможно, используется достаточно большой (низкое разрешение) уровень MIP, который достаточно консервативен.

Видимые и скрытые объекты записываются в буферы.

Последние, как мне кажется, нужны для выполнения запросов на окклюзию: мы сообщаем CPU, что какой-то объект не виден, и прекращаем его обработку до тех пор, пока он не станет видимым.

Видимые объекты также добавляются в список кандидатов на рендеринг.



Постоянный скрининг

Судя по всему, постоянное отбрасывание связано с потоковой передачей.

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

Это довольно сложный шейдер, но глядя на его входные и выходные данные можно понять, что он пишет, сколько кластеров треугольников каждого типа (для традиционной растеризации и растеризации с использованием вычислительных шейдеров) видно в буфере MainRasterizeArgsSWHW (растеризация SW в вычислительном шейдер, HW — обычная растеризация).



Общий обзор Нанита



Кластеризация и уровни детализации

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

Хотя некоторые люди предполагалось Применение геометрические изображения как способа перетекания друг в друга уровней детализации, я здесь ничего подобного не вижу.

Треугольники сгруппированы в участки, которые здесь называются кластерами.

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

Техника кластеризации уже была описана ранее в статьях из Юбисофт И Обморожение .

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

Здесь используются некоторые сложные магические заклинания, чтобы грозди соединялись друг с другом без швов.



Растеризация

В захваченном кадре есть два типа растеризации: на вычислительных шейдерах и традиционная с вызовами отрисовки.

Буферы, полученные на предыдущем шаге, содержат аргументы для выполнения косвенного рендеринга.



Общий обзор Нанита

Нарисуйте 3333 экземпляра по 384 вершины в каждом.

Запустите 34821 группу для вычислительного шейдера.

Первый вызов отрисовки использует традиционную аппаратную растеризацию.

Критерий предпочтения аппаратной или программной растеризации не очень ясен, но можно предположить, что он связан с размером треугольников относительно пикселей.

Ранее Эпический отмеченный что растеризация в вычислительных шейдерах в некоторых сценариях может превосходить аппаратную растеризацию, а в других предпочтительнее аппаратный подход. Эти сценарии связаны с тем, как аппаратная растеризация увязает в большом количестве очень маленьких треугольников, поскольку не может эффективно планировать их обработку.

Страдает как загрузка видеокарты, так и ее производительность.

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



Общий обзор Нанита

Информация выше показывает нам размер кластеров (384 вершины, т.е.

128 треугольников).

Он подозрительно хорошо делится на 32 и 64, которые обычно выбираются для эффективного заполнения деформации графического процессора.

Таким образом, 3333 кластера рисуются аппаратно, а программная растеризация заботится об остальной геометрии Nanite. Каждая группа состоит из 128 потоков, поэтому я предполагаю, что каждый поток обрабатывает 1 треугольник (каждый кластер состоит из 128 треугольников).

Колоссальные 5 миллионов треугольников! Эти цифры говорят о том, что более 90% геометрии растрируется в программном обеспечении, что подтверждает слова Брайана Кэриса.

Здесь .

Для теней повторяется тот же процесс, только все отрисовывается в буфер глубины.

Вышеописанный процесс повторяется для некоторой геометрии в Post Pass. Видимо причина этого в том, что Nanite создает более актуальный иерархический буфер глубины (в BuildPreviousOccluderHZB) с информацией о глубине текущего кадра, полученной на этом этапе, объединяет ее с информацией из ZPrepass (проход, выполняемый перед Nanite стартует) и использует все эти данные для более корректного скрининга.

Мне интересно, как происходит отсечение геометрии, которая была по краям буфера глубины из предыдущего кадра или не была видна в предыдущем кадре, чтобы не было артефактов с резким появлением объектов.

В любом случае результатом этапа растеризации является единая текстура, о которой мы поговорим дальше.



Буфер видимости

Одной из главных особенностей Nanite является буфер видимости.

Это текстура формата R32G32_UINT, которая содержит информацию о треугольнике и глубине для каждого пикселя.

На этом этапе информации о материалах еще нет и данные первого канала содержат информацию, необходимую для получения свойств материала в будущем.

Буфер видимости — не новая идея.

Это уже было описано ранее (например, Здесь И Здесь ), но насколько я знаю коммерческих игр с ним не было.

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

Буфер видимости организован следующим образом:

Р[31:7] Р[6:0] г
ClusterID (25 бит) Идентификатор треугольника (7 бит) 32-битная глубина


Общий обзор Нанита

Идентификаторы кластера

Общий обзор Нанита

Идентификаторы треугольников

Общий обзор Нанита

Глубина Это поднимает верхний предел примерно в 4 миллиарда (232) треугольников, чего раньше было вполне достаточно, но сейчас я уже в этом не уверен.

Что мне интересно, так это то, насколько ограничена эта информация.

Другой Подходы к буферу видимости предлагаю хранить барицентрические координаты.

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

Это подробно описано Здесь .

И последнее замечание: вы можете заметить пробел, где должен стоять персонаж, что позволяет оценить эффективность системы проверки.



Нанит::EmitDepthTargets

В конце этого этапа у нас будет три типа информации: глубина, векторы движения и «глубина материала».

Первые два — это стандартные значения, которые будут использоваться позже, например для TAA, отражений и т. д. Также есть интересная текстура под названием Nanite Mask, которая просто показывает, где была отрисована геометрия Nanite. И она выглядит так:

Общий обзор Нанита

Нанитовая маска

Общий обзор Нанита

Векторы движения нанитов

Общий обзор Нанита

Глубина сцены

Глубина материала

Однако наиболее интересные данные в выходной текстуре на этом этапе — это глубина материала.

По сути, это идентификатор материала, преобразованный в значение глубины и сохраненный в буфере глубины.

Таким образом, каждый материал имеет свой оттенок серого.

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



Общий обзор Нанита



Нанит::BasePass

К счастью, теперь у нас есть хорошее понимание геометрического конвейера.

До сих пор мы вообще не говорили о материалах.

Это довольно интересно, потому что между генерацией буфера видимости и текущим моментом много времени тратится на другие вещи: сетку освещения, атмосферу и т.д., а также отрисовывается G-буфер, как это обычно бывает. На самом деле буфер видимости предназначен для разделения геометрии и материалов.

Важные шаги происходят внутри Classify Materials и Emit G-buffer.

Общий обзор Нанита



Классифицировать материалы

На этапе классификации материала запускается вычислительный шейдер, который анализирует буфер полноэкранной видимости.

Это очень важно для следующего прохода.

Результатом этого прохода является текстура размером 20x12 (=240) пикселей в формате R32G32_UINT, называемая Material Range. Он кодирует диапазон материалов размером 64x64, представленных в регионе.

Это выглядит так:

Общий обзор Нанита



Эмитировать G-буфер

Мы наконец достигли точки, где видимость и материалы встречаются.

Здесь информация о треугольниках из буфера видимости преобразуется в свойства поверхности.

Unreal позволяет пользователям назначать поверхностям произвольные материалы.

Как же эффективно справиться с этой сложностью? Вот как выглядит G-буфер Emit.

Общий обзор Нанита

Мы видим то, что выглядит как вызовы отрисовки для каждого идентификатора материала, и каждый вызов отрисовки делит экран на 240 квадратов, которые рисуются на экране.

Полноэкранный рендеринг для каждого материала? Они сумасшедшие? Не совсем.

Ранее мы отмечали, что текстура с разрывами материала состоит из 240 текселей, поэтому каждый квад такого полноэкранного прохода соответствует одному текселу.

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

Если нет, то координата x вершины устанавливается в NaN и весь четырехугольник отбрасывается.

Этот известная операция .

Насколько я могу судить, система использует 14 бит для идентификатора материала.

Максимум 16384 материалов.

Идентификатор материала передается через буфер констант в вершинный шейдер, и вы можете проверить, попадает ли он в диапазон материала.

Кроме того, помните, что мы создали текстуру с глубиной материала, где каждый идентификатор материала установлен как глубина.

Квадраты сохраняют номер материала как глубину, и выполняется проверка глубины, которая устанавливается на равенство.

Таким образом, оборудование может очень быстро отбросить все ненужные пиксели.

В качестве дополнительного шага движок имеет предварительно размеченный трафаретный буфер, в котором отмечены пиксели, содержащие геометрию Nanite. Это позволяет использовать ранний тест трафарета в качестве оптимизации.

Чтобы понять, что все это значит, посмотрите на буфер альбедо.



Общий обзор Нанита

Применение сетчатого материала Нанит

Общий обзор Нанита

Тест глубины нанитового материала

Общий обзор Нанита

Второе сетчатое применение нанитовых материалов

Общий обзор Нанита

Глубинное испытание второго нанитового материала

Общий обзор Нанита

Новейшее применение сетки из материала Nanite

Общий обзор Нанита

Последний тест глубины нанита

Общий обзор Нанита

Альбедо Нанит Возможно, вы заметили, что некоторые квадраты полностью красные.

Я предполагал, что они будут полностью отброшены в вершинном шейдере.

Однако я думаю, что текстура с разрывом материалов не просто так называется и это именно разрыв материалов, которые есть в плитке.

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

В любом случае, это основная идея.

Тот же процесс, показанный на изображениях, повторяется для всех материалов.

Итоговый G-буфер выглядит как на плитках ниже.



Общий обзор Нанита



Заключительные замечания



Общий обзор Нанита

На этом этапе Nanite завершается, и последующий конвейер рендеринга переключается на обычное отложенное освещение.

Проделанная работа действительно замечательна.

Я уверен, что есть еще много деталей, о которых я не знаю, и неточностей в том, как это работает. Так что я с нетерпением жду того, что скажет Брайан Кэрис на SIGGRAPH в этом году.

Теги: #Разработка игр #Работа с 3D графикой #unreal engine #компьютерная графика #Nanite #nanite #Анализ кадров

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.