Как виртуальное текстурирование может быть эффективным?

27

Для справки я имею в виду «общее название» для техники, впервые (я полагаю), представленной в технологии MegaTexture от idTech 5 . Смотрите видео здесь для быстрого взгляда на то, как это работает.

В последнее время я просматривал некоторые статьи и публикации, связанные с этим, и я не понимаю, как это может быть эффективным. Разве это не требует постоянного пересчета UV-координат из пространства «глобальной страницы текстуры» в виртуальные координаты текстуры? И как это не ограничивает большинство попыток пакетной геометрии в целом? Как это может позволить произвольное увеличение? Разве в какой-то момент не потребовалось бы подразделить полигоны?

Просто я так много не понимаю, и я не смог найти по-настоящему легко доступных ресурсов по этой теме.

Llamageddon
источник

Ответы:

20

обзор

Основная причина виртуального текстурирования (VT) или разреженных виртуальных текстур , как его иногда называют, заключается в оптимизации памяти. Суть в том, чтобы перемещать в видеопамять только те тексели (обобщенные как страницы / фрагменты), которые вам могут понадобиться для визуализированного кадра. Таким образом, это позволит вам иметь гораздо больше текстурных данных в автономном или медленном хранилище (HDD, Optical-Disk, Cloud), чем в противном случае поместится в видеопамять или даже основную память. Если вы понимаете концепцию виртуальной памяти, используемой современными операционными системами, это то же самое по своей сути (название не дано случайно).

VT не требует повторного вычисления UV в том смысле, что вы должны делать это каждый кадр перед рендерингом меша, а затем повторно передавать данные вершин, но для этого требуется некоторая существенная работа в шейдерах Vertex и Fragment для выполнения косвенного поиска от входящих UV. Однако в хорошей реализации оно должно быть полностью прозрачным для приложения, если оно использует виртуальную текстуру или традиционную. На самом деле, в большинстве случаев приложение будет смешивать оба типа текстурирования, виртуальное и традиционное.

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

Проблемы с VT

Увеличение / уменьшение и резкое движение камеры - самые сложные вещи в настройке VT. Это может выглядеть очень привлекательно для статичной сцены, но как только вещи начнут двигаться, будет запрошено больше страниц / плиток текстуры, чем вы можете передать для внешнего хранения. Асинхронный ввод-вывод файлов и многопоточность могут помочь, но если это система реального времени, как в игре, вам просто нужно будет рендерить несколько кадров с плитками с более низким разрешением, пока не появятся изображения с высоким разрешением, время от времени , в результате чего получается размытая текстура. Здесь нет серебряной пули, и это самая большая проблема с техникой, ИМО.

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

В общем, VT интересен, но я бы не рекомендовал его всем. Он может хорошо работать, но его трудно реализовать и оптимизировать, плюс на мой вкус нужно слишком много угловых случаев и настроек для конкретного случая. Но для больших игр с открытым миром или приложений для визуализации данных, это может быть единственно возможным подходом - поместить весь контент в доступное оборудование. С большой работой можно заставить работать довольно эффективно даже на ограниченном оборудовании, как мы видим в версиях id Rage для PS3 и XBOX360 .

Реализация

Мне удалось в некоторой степени заставить VT работать на iOS с OpenGL-ES. Моя реализация не является "отправляемой", но я мог бы сделать это, если бы захотел и имел ресурсы. Вы можете просмотреть исходный код здесь , это может помочь получить лучшее представление о том, как части сочетаются друг с другом. Вот видео демонстрации, работающей на iOS Sim. Это выглядит очень запаздывающим, потому что симулятор ужасен при эмуляции шейдеров, но работает на устройстве плавно.

На следующей диаграмме представлены основные компоненты системы в моей реализации. Он немного отличается от демонстрации SVT Шона (ссылка внизу), но по архитектуре он ближе к тому, что представлен в статье « Ускорение виртуального текстурирования с использованием CUDA» , описанной в первой книге GPU Pro (ссылка ниже).

виртуальная система текстурирования

  • Page FilesЭто виртуальные текстуры, уже разрезанные на листы (страницы AKA) в качестве шага предварительной обработки, поэтому они готовы для перемещения с диска в видеопамять при необходимости. Файл страницы также содержит полный набор мип-карт, также называемых виртуальными мип-картами .

  • Page Cache Managerсохраняет приложения бокового представления Page Tableи Page Indirectionтекстур. Поскольку перемещение страницы из автономного хранилища в память обходится дорого, нам необходим кэш, чтобы избежать перезагрузки того, что уже доступно. Этот кеш является очень простым кешем с наименьшим количеством использованных ресурсов (LRU). Кэш также является компонентом, отвечающим за поддержание физических текстур в актуальном состоянии с помощью собственного локального представления данных.

  • Это Page Providerасинхронная очередь заданий, которая извлекает страницы, необходимые для данного вида сцены, и отправляет их в кэш.

  • Page IndirectionТекстура текстуры с одного пикселя для каждой страницы / плитки в виртуальной текстуры, которая будет картировать входящие UVs к Page Tableтекстуре кэша , который имеет фактические данные текселя. Эта текстура может быть довольно большой, поэтому она должна использовать какой-то компактный формат, например RGBA 8: 8: 8: 8 или RGB 5: 6: 5.

Но нам все еще не хватает ключевой части здесь, и именно так определяется, какие страницы должны быть загружены из хранилища в кеш и, следовательно, в Page Table. Вот где проходит обратная связь и Page Resolverвход.

Пропуск обратной связи - это предварительный рендеринг вида с пользовательским шейдером и гораздо более низким разрешением, который записывает идентификаторы требуемых страниц в цветной кадровый буфер. Эта цветная мозаика куба и сферы выше - это реальные индексы страниц, закодированные в цвете RGBA. Этот предварительный рендеринг затем считывается в основную память и обрабатывается Page Resolverдля декодирования индексов страниц и запуска новых запросов с помощью Page Provider.

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

Другие ресурсы стоит проверить

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

glampert
источник
Привет, спасибо за отличный ответ. Я знаю, что это, как правило, осуждается, но у меня есть различные проблемы, поэтому я в основном просто разбираюсь в вещах - чтобы получить интуитивно понятный обзор тем на будущее (боюсь, что на данный момент изучение и реализация вещей мне не по силам ) - в любом случае, если возможно, не могли бы вы опубликовать пример псевдокода, описывающий сам процесс, в идеале, но не обязательно, иллюстрированный?
Лламагеддон
1
@Llamageddon, просто так получилось, что у меня все еще была под рукой диаграмма;) Боюсь, псевдокод будет немного сложным для предоставления, поскольку в нем довольно много реального кода. Но я надеюсь, что расширенный ответ поможет дать общее представление о технике.
гламперт
3
Стоит отметить, что большинство современного оборудования теперь предоставляет программируемые таблицы страниц, что устраняет необходимость в текстуре перенаправления. Это раскрывается, например, через зарезервированные ресурсы directx12 , которые основаны на плиточных ресурсах directx11 или разреженных текстурах opengl .
MooseBoys
1
@Llamageddon, предварительный проход обратной связи можно выполнить при более низком разрешении, чтобы сэкономить как можно больше вычислений и памяти, поскольку пиксели для страницы обычно повторяются (вы можете заметить большие цветные квадраты в моей демонстрации). Вы правы в том, что в конечном итоге она может пропустить такую ​​видимую страницу, но это обычно не будет иметь большого визуального эффекта, потому что система всегда должна сохранять в кэше по крайней мере самое низкое значение из всех имеющихся VT. Во второй статье, на которую я ссылаюсь, есть все примеры шейдеров в приложении, вы также можете обратиться к репозиторию для моего собственного проекта, они похожи.
гламперт
1
@glampert Ааа, понятно; в этом есть смысл. Тем не менее, я думаю, что есть много вариантов для обработки прозрачных пленок; на проходе идентификатора страницы вы могли бы размываться (таким образом, гистограмма будет видеть все страницы, если не было огромного количества прозрачных слоев), или использовать подход k-буфера , или даже просто основывать резидентность прозрачной текстуры, на которой объекты находятся рядом с камера (в отличие от рендеринга в проходе обратной связи).
Натан Рид
11

Виртуальное текстурирование - логическая крайность текстурных атласов.


Текстурный атлас - это гигантская текстура, в которой содержатся текстуры для отдельных сеток:

Пример текстурного атласа

Текстурные атласы стали популярными благодаря тому, что изменение текстур приводит к полной очистке конвейера на GPU. При создании ячеек UV сжимаются / сдвигаются так, чтобы они представляли правильную «часть» всего текстурного атласа.

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

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

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

Есть несколько способов реализовать это, но я объясню реализацию, описанную Шоном Барреттом в его выступлении на GDC . (который я очень рекомендую смотреть)

У нас есть три основных элемента: виртуальная текстура, физическая текстура и таблица соответствия.

Виртуальная текстура

Виртуальная текстура представляет теоретический мега атлас, который был бы у нас, если бы у нас было достаточно vram, чтобы вместить все. Это на самом деле не существует в памяти нигде. Физическая текстура представляет, какие данные о пикселях у нас есть в vram Таблица поиска - это отображение между ними. Для удобства мы разбиваем все три элемента на плитки одинакового размера или страницы.

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

Во-первых, нам нужно найти местоположение страницы в физической текстуре. Затем нам нужно рассчитать местоположение УФ на странице. Наконец, мы можем добавить эти два смещения вместе, чтобы получить местоположение УФ в физической текстуре

float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;


Расчет страницыLocInPhysicalTex

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

float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);


Расчет inPageLocation

inPageLocation - это UV-координата, относящаяся к верхнему левому углу страницы, а не к верхнему левому краю всей текстуры.

Один из способов рассчитать это - вычесть ультрафиолетовое излучение в верхней левой части страницы, а затем масштабировать до размера страницы. Тем не менее, это довольно немного математики. Вместо этого мы можем использовать представление IEEE с плавающей запятой. IEEE с плавающей запятой хранит дробную часть числа в виде серии из 2 основных дробей.

введите описание изображения здесь

В этом примере число:

number = 0 + (1/2) + (1/8) + (1/16) = 0.6875

Теперь давайте посмотрим на упрощенную версию виртуальной текстуры:

Простая виртуальная текстура

1/2 бит говорит нам, находимся ли мы в левой половине текстуры или справа. 1/4 бита говорит нам, в какой четверти половины мы находимся. В этом примере, поскольку текстура разбита на 16 или 4 в сторону, эти первые два бита говорят нам, на какой странице мы находимся. биты говорят нам расположение внутри страницы.

Мы можем получить оставшиеся биты, сдвинув float с помощью exp2 () и удалив их с помощью fract ()

float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);

Где numTiles - это int2, указывающая количество плиток на каждой стороне текстуры. В нашем примере это будет (4, 4)

Итак, давайте рассчитаем inPageLocation для зеленой точки, (x, y) = (0,6875, 0,375)

inPageLocation = float2(0.6875, 0.375) * exp2(sqrt(int2(4, 4));
               = float2(0.6875, 0.375) * int2(2, 2);
               = float2(1.375, 0.75);

inPageLocation = fract(float2(1.375, 0.75));
               = float2(0.375, 0.75);

Последнее, что нужно сделать, прежде чем мы закончим. В настоящее время inPageLocation представляет собой UV-координату в виртуальной текстуре «space». Тем не менее, мы хотим, чтобы координата UV в физической текстуре «пространство». Для этого нам просто нужно масштабировать inPageLocation по отношению размера виртуальной текстуры к физическому размеру текстуры.

inPageLocation *= physicalTextureSize / virtualTextureSize;



Итак, готовая функция:

float2 CalculatePhysicalTexUV(float2 virtTexUV, Texture2D<float2> lookupTable, uint2 physicalTexSize, uint2 virtualTexSize, uint2 numTiles) {
    float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);

    float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
    inPageLocation = fract(inPageLocation);
    inPageLocation *= physicalTexSize / virtualTexSize;

    return pageLocInPhysicalTex + inPageLocation;
}
RichieSams
источник
Я не имею в виду виртуальное текстурирование, наиболее известное как технология MegaTexture от idTech 5 . Также увидеть это и это . Я видел это упомянутое в обзоре многих современных конвейеров рендеринга движков, и в нескольких статьях, которые используют подобный подход для теневых карт. Он имеет много общего с текстурными атласами, да, он использует их по-своему, но я не путаю его с текстурными атласами.
Лламагеддон
Ааа. Спасибо за ссылки. Можете ли вы добавить их к вопросу. Я
обновлю
3
IMO, главный недостаток простых текстурных атласов (не виртуальных текстур) состоит в том, что вы теряете режимы обтекания, такие как повтор и зажим, и кровотечение происходит из-за фильтрации / отображения, а не точности с плавающей точкой. Я был бы удивлен, увидев, что точность с плавающей точкой становится проблемой для обычных (не виртуальных) текстур; даже 16-килобайтная текстура (максимум, допустимый текущими API-интерфейсами) не достаточно велика, чтобы действительно снизить точность плавающего изображения.
Натан Рид
@RichieSams Кстати, я думаю, что ваш ответ хороший, даже если на другой вопрос. Вы должны сделать пост Q & A.
Лламагеддон
Хм, это объясняет это довольно хорошо, хотя я не очень понимаю, как это работает с уровнями MIP. Я хотел бы записать свою конкретную проблему с пониманием этого, но это как бы ускользает от меня ...
Лламагеддон