Wirtualne teksturowanie jest logiczną skrajnością atlasów tekstur.
Atlas tekstur to pojedyncza olbrzymia tekstura, która zawiera w sobie tekstury dla poszczególnych siatek:
Atlasy tekstur stały się popularne ze względu na fakt, że zmiana tekstur powoduje pełne opróżnienie potoku na GPU. Podczas tworzenia siatek promienie UV są kompresowane / przesuwane, tak aby reprezentowały prawidłową „część” całego atlasu tekstur.
Jak wspomniano w komentarzach @ nathan-reed, jedną z głównych wad atlasów tekstur jest utrata trybów zawijania, takich jak powtarzanie, zaciskanie, obramowanie itp. Ponadto, jeśli tekstury nie mają wystarczającej obramowania wokół nich, możesz przypadkowo próbkuj z sąsiedniej tekstury podczas filtrowania. Może to prowadzić do krwawienia artefaktów.
Atlasy tekstur mają jedno główne ograniczenie: rozmiar. Graficzne interfejsy API nakładają miękki limit na wielkość tekstury. To powiedziawszy, pamięć graficzna jest tylko tak duża. Istnieje więc sztywny limit rozmiaru tekstury, podany przez rozmiar twojego v-ram. Tekstury wirtualne rozwiązują ten problem, zapożyczając koncepcje z pamięci wirtualnej .
Tekstury wirtualne wykorzystują fakt, że w większości scen widać tylko niewielką część wszystkich tekstur. Tak więc tylko ten podzbiór tekstur musi znajdować się w vram. Reszta może znajdować się w głównej pamięci RAM lub na dysku.
Istnieje kilka sposobów na jego wdrożenie, ale wyjaśnię implementację opisaną przez Seana Barretta w jego przemówieniu na GDC . (które bardzo polecam oglądać)
Mamy trzy główne elementy: teksturę wirtualną, teksturę fizyczną i tabelę odnośników.
Wirtualna tekstura reprezentuje teoretyczny mega atlas, który mielibyśmy, gdybyśmy mieli wystarczająco dużo vramu, aby zmieścić wszystko. Nigdzie nie istnieje w pamięci. Fizyczna tekstura reprezentuje, jakie dane pikseli faktycznie mamy w vramie. Tabela przeglądowa jest mapowaniem między nimi. Dla wygody dzielimy wszystkie trzy elementy na równe rozmiary płytek lub stron.
Tabela przeglądowa przechowuje położenie lewego górnego rogu płytki w fakturze fizycznej. Zatem, biorąc pod uwagę UV dla całej wirtualnej tekstury, w jaki sposób otrzymujemy odpowiedni UV dla tekstury fizycznej?
Najpierw musimy znaleźć lokalizację strony w strukturze fizycznej. Następnie musimy obliczyć lokalizację UV na stronie. Wreszcie możemy dodać te dwa przesunięcia razem, aby uzyskać lokalizację promieniowania UV w strukturze fizycznej
float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;
Obliczanie pageLocInPhysicalTex
Jeśli sprawimy, że tabela odnośników ma ten sam rozmiar co liczba płytek w wirtualnej teksturze, możemy po prostu próbkować tabelę odnośników z próbkowaniem najbliższego sąsiada i uzyskamy lokalizację lewego górnego rogu strony w obrębie tekstury fizycznej.
float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);
Obliczanie inPageLocation
inPageLocation to współrzędna UV względem lewego górnego rogu strony, a nie lewego górnego rogu całej tekstury.
Jednym ze sposobów obliczenia tego jest odjęcie UV w lewym górnym rogu strony, a następnie skalowanie do rozmiaru strony. To jednak dość matematyka. Zamiast tego możemy wykorzystać sposób reprezentacji zmiennoprzecinkowej IEEE. Zmienny punkt IEEE przechowuje ułamkową część liczby przez serię ułamków podstawowych 2.
W tym przykładzie liczba to:
number = 0 + (1/2) + (1/8) + (1/16) = 0.6875
Teraz spójrzmy na uproszczoną wersję wirtualnej tekstury:
1/2 bitu mówi nam, czy jesteśmy w lewej połowie tekstury, czy w prawej. 1/4 bitu mówi nam, w której ćwiartce się znajdujemy. W tym przykładzie, ponieważ tekstura jest podzielona na 16 lub 4 na bok, te dwa pierwsze bity mówią nam, na której stronie jesteśmy. bity informują nas o lokalizacji na stronie.
Pozostałe bity możemy uzyskać, przesuwając liczbę zmiennoprzecinkową za pomocą exp2 () i usuwając je za pomocą funkcji fract ()
float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);
Gdzie numTiles jest liczbą całkowitą 2, podającą liczbę płytek na stronę tekstury. W naszym przykładzie byłoby to (4, 4)
Obliczmy więc inPageLocation dla zielonego punktu, (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);
Jeszcze jedna rzecz do zrobienia, zanim skończymy. Obecnie inPageLocation jest współrzędną UV w wirtualnej „przestrzeni” tekstury. Chcemy jednak współrzędnej UV w „przestrzeni” tekstury fizycznej. Aby to zrobić, wystarczy skalować inPageLocation według stosunku wirtualnego rozmiaru tekstury do fizycznego rozmiaru tekstury
inPageLocation *= physicalTextureSize / virtualTextureSize;
Tak więc gotowa funkcja to:
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;
}