W jaki sposób wirtualne teksturowanie może być skuteczne?

27

Dla odniesienia mam na myśli „ogólną nazwę” techniki wprowadzonej po raz pierwszy (jak sądzę) w technologii MegaTexture idTech 5 . Zobacz wideo tutaj, aby szybko zobaczyć, jak to działa.

Ostatnio przeglądałem niektóre artykuły i publikacje z tym związane, a nie rozumiem, jak to może być skuteczne. Czy nie wymaga ciągłego przeliczania współrzędnych UV z przestrzeni „globalnej strony tekstury” na wirtualne współrzędne tekstury? A w jaki sposób nie ogranicza to większości prób grupowania geometrii? Jak może pozwolić na dowolne powiększanie? Czy w pewnym momencie nie wymagałoby dzielenia wielokątów?

Jest tyle rzeczy, których nie rozumiem i nie byłem w stanie znaleźć żadnych łatwo dostępnych zasobów na ten temat.

Llamageddon
źródło

Odpowiedzi:

20

Przegląd

Głównym powodem wirtualnych tekstur (VT) lub rzadkich wirtualnych tekstur , jak się je czasami nazywa, jest optymalizacja pamięci. Istotą tego jest przeniesienie do pamięci wideo tylko faktycznych tekstur (uogólnionych jako strony / kafelki), które mogą być potrzebne do renderowanej klatki. Dzięki temu będziesz mieć znacznie więcej danych tekstur w trybie offline lub wolnej pamięci (HDD, dysk optyczny, chmura) niż w innym przypadku w pamięci wideo lub nawet w pamięci głównej. Jeśli rozumiesz koncepcję wirtualnej pamięci używanej przez nowoczesne systemy operacyjne, to w istocie jest to samo (nazwa nie została nadana przypadkowo).

VT nie wymaga ponownego obliczania promieniowania UV w tym sensie, że zrobiłbyś to każdą klatkę przed renderowaniem siatki, a następnie ponownie przesłał dane wierzchołków, ale wymaga znacznej pracy w modułach cieniujących wierzchołki i fragmenty, aby wykonać pośrednie wyszukiwanie z przychodzących promieni UV. Jednak w dobrej implementacji aplikacja powinna być całkowicie przezroczysta, jeśli używa wirtualnej lub tradycyjnej tekstury. W rzeczywistości przez większość czasu aplikacja będzie mieszać oba rodzaje teksturowania, wirtualny i tradycyjny.

Dozowanie może teoretycznie działać bardzo dobrze, chociaż nigdy nie przyjrzałem się szczegółom tego. Ponieważ typowymi kryteriami grupowania geometrii są tekstury, a dzięki VT każdy wielokąt w scenie może mieć tę samą „nieskończenie dużą” teksturę, teoretycznie można uzyskać pełny rysunek sceny za pomocą 1 wywołania losowania. Ale w rzeczywistości pojawiają się inne czynniki, które sprawiają, że jest to niepraktyczne.

Problemy z VT

Przybliżanie / oddalanie i nagły ruch kamery to najtrudniejsze rzeczy w obsłudze VT. Może to wyglądać bardzo atrakcyjnie dla sceny statycznej, ale gdy wszystko zacznie się poruszać, zostanie zażądanych więcej stron / kafelków z teksturami, niż możesz przesyłać strumieniowo do pamięci zewnętrznej. Pomocne operacje we / wy pliku asynchronicznego i wątki mogą pomóc, ale jeśli jest to system czasu rzeczywistego, tak jak w grze, będziesz musiał renderować tylko kilka klatek z kafelkami o niższej rozdzielczości, aż pojawią się te w wysokiej rozdzielczości, co jakiś czas , co powoduje rozmycie tekstury. Nie ma tu srebrnej kuli i to jest największy problem z techniką, IMO.

Wirtualne teksturowanie również nie obsługuje przezroczystości w łatwy sposób, więc przezroczyste wielokąty potrzebują dla nich osobnej tradycyjnej ścieżki renderowania.

Podsumowując, VT jest interesujące, ale nie poleciłbym go wszystkim. Może działać dobrze, ale trudno jest go wdrożyć i zoptymalizować, a do mojego gustu jest po prostu zbyt wiele przypadków narożnych i dostosowań specyficznych dla poszczególnych przypadków. Ale w przypadku dużych gier z otwartym światem lub aplikacji do wizualizacji danych może to być jedyne możliwe podejście, aby dopasować całą zawartość do dostępnego sprzętu. Przy dużym nakładzie pracy można go uruchomić dość wydajnie nawet na ograniczonym sprzęcie, jak widać w wersjach id Rage id na PS3 i XBOX360 .

Realizacja

W pewnym stopniu udało mi się sprawić, że VT działa na iOS z OpenGL-ES. Moje wdrożenie nie jest „możliwe do wysłania”, ale można sobie wyobrazić, że gdybym chciał i miał zasoby. Możesz zobaczyć kod źródłowy tutaj , może to pomóc w lepszym wyobrażeniu o tym, jak elementy pasują do siebie. Oto wideo demonstracji uruchomionej na iOS Sim. Wygląda na bardzo opóźniony, ponieważ symulator nie potrafi emulować shaderów, ale działa płynnie na urządzeniu.

Poniższy schemat przedstawia główne elementy systemu w mojej implementacji. Różni się on nieco od demonstracji Seana SVT (link poniżej), ale pod względem architektury jest bliżej architektury przedstawionej w artykule Accelerating Virtual Texturing using CUDA , znalezionym w pierwszej książce GPU Pro (link poniżej).

wirtualny system teksturowania

  • Page Filesto wirtualne tekstury, już pocięte na kafelki (strony AKA) jako etap wstępnego przetwarzania, dzięki czemu można je przenieść z dysku do pamięci wideo w razie potrzeby. Plik stronicowania zawiera również cały zestaw mipmap, zwanych także wirtualnymi mipmapami .

  • Page Cache Managerzachowuje reprezentację tekstur Page Tablei po stronie aplikacji Page Indirection. Ponieważ przenoszenie strony z pamięci offline do pamięci jest kosztowne, potrzebujemy pamięci podręcznej, aby uniknąć ponownego ładowania tego, co jest już dostępne. Ta pamięć podręczna jest bardzo prostą pamięcią podręczną ostatnio używaną (LRU). Pamięć podręczna jest również składnikiem odpowiedzialnym za aktualizowanie fizycznych tekstur z ich własną lokalną reprezentacją danych.

  • Page ProviderJest asynchroniczne kolejki zadań, które będą pobierać stron potrzebnych dla danego widoku sceny i wysłać je do pamięci podręcznej.

  • Page IndirectionStruktura jest struktura z jednego piksela na każdej stronie / płytki w wirtualnym tekstury, która będzie mapować przychodzące UVS do Page Tabletekstury podręcznej, która ma rzeczywiste dane Texel. Ta tekstura może stać się dość duża, więc musi używać kompaktowego formatu, takiego jak RGBA 8: 8: 8: 8 lub RGB 5: 6: 5.

Ale wciąż brakuje nam tutaj kluczowego elementu i tak można ustalić, które strony należy załadować z pamięci do pamięci podręcznej, a tym samym do Page Table. Właśnie tam Feedback Pass i Page Resolverenter.

Sprzężenie zwrotne jest wstępnym renderowaniem widoku, z niestandardowym modułem cieniującym i przy znacznie niższej rozdzielczości, który zapisze identyfikatory wymaganych stron do kolorowego bufora ramki. Ten kolorowy patchwork powyżej sześcianu i kuli to rzeczywiste indeksy stron zakodowane jako kolor RGBA. To renderowanie przed przejściem jest następnie wczytywane do pamięci głównej i przetwarzane przez Page Resolverdekodowanie indeksów stron i uruchamianie nowych żądań za pomocą Page Provider.

Po wstępnym przejściu sprzężenia zwrotnego scenę można renderować normalnie za pomocą shaderów wyszukiwania VT. Pamiętaj jednak, że nie czekamy na zakończenie żądania nowej strony, byłoby to straszne, ponieważ po prostu blokowalibyśmy synchroniczne operacje we / wy pliku. Żądania są asynchroniczne i mogą, ale nie muszą być gotowe do czasu renderowania ostatecznego widoku. Jeśli są gotowe, słodkie, ale jeśli nie, zawsze przechowujemy zablokowaną stronę mipmapy o niskiej rozdzielczości w pamięci podręcznej jako rezerwową, więc mamy tam trochę danych tekstur do wykorzystania, ale będzie rozmazana.

Inne zasoby, które warto sprawdzić

VT jest wciąż dość gorącym tematem w grafice komputerowej, więc dostępnych jest mnóstwo dobrych materiałów, powinieneś być w stanie znaleźć znacznie więcej. Jeśli mogę dodać coś jeszcze do tej odpowiedzi, możesz zapytać. Jestem trochę zardzewiały na ten temat, nie czytałem dużo o nim przez ostatni rok, ale zawsze dobrze jest, aby pamięć ponownie go odwiedzała :)

glampert
źródło
Hej, dziękuję za doskonałą odpowiedź. Wiem, że zwykle jest to niezadowolone, ale mam różne problemy, więc przeważnie przeglądam różne rzeczy - aby uzyskać intuicyjny przegląd tematów na przyszłość (obawiam się, że odpowiednie uczenie się i wdrażanie rzeczy jest obecnie poza moim zasięgiem ) - w każdym razie, jeśli to możliwe, czy mógłbyś zamieścić przykład pseudokodu opisujący sam proces, idealnie, ale niekoniecznie, zilustrowany?
Llamageddon,
1
@Lamageddon, tak się składa, że ​​wciąż mam pod ręką diagram;) Obawiam się, że pseudo-kod będzie nieco trudny do dostarczenia, ponieważ zawiera sporo prawdziwego kodu. Ale mam nadzieję, że rozszerzona odpowiedź pomoże dać ogólne wyobrażenie o tej technice.
glampert,
3
Warto zauważyć, że większość współczesnego sprzętu odsłania programowalne tabele stron, eliminując potrzebę tekstury przekierowania. To jest wystawiony przez np directx12 zasobów zarezerwowanych , który opiera się na DirectX11 kafelki zasobów lub OpenGL nielicznych tekstur .
MooseBoys,
1
@ Lamageddon, wstępny przebieg sprzężenia zwrotnego można wykonać w niższej rozdzielczości, aby zaoszczędzić jak najwięcej obliczeń i pamięci, ponieważ piksele na stronie zwykle się powtarzają (można zauważyć duże kolorowe kwadraty w mojej wersji demonstracyjnej). Masz rację, że w końcu może przegapić taką widoczną stronę, ale zwykle nie będzie to miało dużego wpływu wizualnego, ponieważ system powinien zawsze utrzymywać przynajmniej najniższą mipmapę z całego VT dostępną w pamięci podręcznej. Ten drugi artykuł, który podłączyłem, zawiera wszystkie przykłady programów do cieniowania w załączniku, możesz też odnieść się do repozytorium mojego projektu, są one podobne.
glampert,
1
@glampert Ahh, rozumiem; to ma sens. Mimo to uważam, że istnieje wiele opcji obsługi folii; w identyfikatorze strony można roztrząsać (aby histogram wyświetlał wszystkie strony, chyba że istnieje ogromna liczba przezroczystych warstw), można zastosować metodę bufora k lub nawet po prostu oprzeć przezroczystą rezydencję tekstury, na której obiekty znajdują się w pobliżu kamera (w przeciwieństwie do renderowania ich w przepustce sprzężenia zwrotnego).
Nathan Reed,
11

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:

Przykład atlasu tekstur

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

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.

wprowadź opis zdjęcia tutaj

W tym przykładzie liczba to:

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

Teraz spójrzmy na uproszczoną wersję wirtualnej tekstury:

Prosta wirtualna tekstura

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;
}
RichieSams
źródło
Nie mam na myśli wirtualnego teksturowania, najbardziej znanego jako technologia MegaTexture idTech 5 . Zobacz także to i to . Widziałem to w przeglądzie wielu współczesnych potoków renderujących silniki oraz w kilku artykułach, które wykorzystują podobne podejście do mapowania cieni. Ma to wiele wspólnego z atlasami tekstur, tak, używa ich w pewien sposób, ale nie mylę go z atlasami tekstur.
Llamageddon,
Ahh Dzięki za linki. Czy możesz dodać je do pytania? Zaktualizuję odpowiednio swoją odpowiedź
RichieSams,
3
IMO, główną wadą prostych atlasów tekstur (nie tekstur wirtualnych) jest to, że tracisz tryby zawijania, takie jak powtarzanie i blokowanie, a krwawienie występuje z powodu filtrowania / mipmapowania - a nie precyzji zmiennoprzecinkowej. Byłbym zaskoczony, gdy precyzja zmiennoprzecinkowa stała się problemem dla zwykłych (nie-wirtualnych) tekstur; nawet tekstura 16K (maksymalna dozwolona przez obecne interfejsy API) nie jest wystarczająco duża, aby naprawdę przeciążać precyzję float.
Nathan Reed,
@RichieSams Btw, myślę, że twoja odpowiedź jest dobra, nawet jeśli na inne pytanie. Powinieneś napisać post na pytania i odpowiedzi.
Llamageddon,
Hmm, to wyjaśnia to całkiem dobrze, chociaż tak naprawdę nie rozumiem, jak to działa z poziomami mip. Chciałbym móc zapisać swój konkretny problem ze zrozumieniem go, ale to mi
umknie