Виртуальное текстурирование - логическая крайность текстурных атласов.
Текстурный атлас - это гигантская текстура, в которой содержатся текстуры для отдельных сеток:
Текстурные атласы стали популярными благодаря тому, что изменение текстур приводит к полной очистке конвейера на 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;
}