La trama virtuale è l'estremo logico degli atlanti delle trame.
Un atlante di trama è una singola trama gigante che contiene trame per singole maglie al suo interno:
Gli atlanti delle texture sono diventati popolari a causa del fatto che la modifica delle trame provoca un flusso completo della pipeline sulla GPU. Quando si creano le mesh, gli UV vengono compressi / spostati in modo da rappresentare la "porzione" corretta dell'intero atlante delle trame.
Come menzionato da @ nathan-reed nei commenti, uno dei principali inconvenienti degli atlanti delle texture sta perdendo le modalità di avvolgimento come ripetizione, pinza, bordo, ecc. Inoltre, se le trame non hanno abbastanza bordo attorno, puoi accidentalmente campione da una trama adiacente quando si esegue il filtraggio. Questo può portare a artefatti sanguinanti.
Gli atlanti delle trame hanno una grande limitazione: le dimensioni. Le API grafiche pongono un limite leggero alla dimensione di una trama. Detto questo, la memoria grafica è solo così grande. Quindi c'è anche un limite rigido alla dimensione della trama, data dalla dimensione della tua V-ram. Le trame virtuali risolvono questo problema, prendendo in prestito concetti dalla memoria virtuale .
Le trame virtuali sfruttano il fatto che nella maggior parte delle scene vedi solo una piccola parte di tutte le trame. Quindi, solo quel sottoinsieme di trame deve essere in vram. Il resto può essere nella RAM principale o sul disco.
Ci sono alcuni modi per implementarlo, ma spiegherò l'implementazione descritta da Sean Barrett nel suo discorso sul GDC . (che consiglio vivamente di guardare)
Abbiamo tre elementi principali: la trama virtuale, la trama fisica e la tabella di ricerca.
La trama virtuale rappresenta il mega atlante teorico che avremmo se avessimo abbastanza vram per adattarci a tutto. In realtà non esiste in memoria da nessuna parte. La trama fisica rappresenta quali dati pixel abbiamo effettivamente in Vram. La tabella di ricerca è il mapping tra i due. Per comodità, suddividiamo tutti e tre gli elementi in tessere o pagine di dimensioni uguali.
La tabella di ricerca memorizza la posizione dell'angolo in alto a sinistra del riquadro nella trama fisica. Quindi, dato un UV all'intera trama virtuale, come possiamo ottenere l'UV corrispondente per la trama fisica?
Innanzitutto, dobbiamo trovare la posizione della pagina all'interno della trama fisica. Quindi abbiamo bisogno di calcolare la posizione dei raggi UV all'interno della pagina. Finalmente possiamo aggiungere questi due offset insieme per ottenere la posizione dei raggi UV all'interno della trama fisica
float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;
Calcolo di pageLocInPhysicalTex
Se rendiamo la tabella di ricerca delle stesse dimensioni del numero di riquadri nella trama virtuale, possiamo semplicemente campionare la tabella di ricerca con il campionamento del vicino più vicino e otterremo la posizione dell'angolo superiore sinistro della pagina all'interno della trama fisica.
float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);
Calcolo inPageLocation
inPageLocation è una coordinata UV relativa alla parte superiore sinistra della pagina, piuttosto che alla parte superiore sinistra dell'intera trama.
Un modo per calcolarlo è sottrarre i raggi UV della parte superiore sinistra della pagina, quindi ridimensionarli in base alla dimensione della pagina. Tuttavia, questo è un bel po 'di matematica. Invece, possiamo sfruttare il modo in cui è rappresentato il virgola mobile IEEE. La virgola mobile IEEE memorizza la parte frazionaria di un numero mediante una serie di frazioni di base 2.
In questo esempio, il numero è:
number = 0 + (1/2) + (1/8) + (1/16) = 0.6875
Ora diamo un'occhiata a una versione semplificata della trama virtuale:
Il 1/2 bit ci dice se siamo nella metà sinistra della trama o a destra. Il 1/4 bit ci dice in quale quarto della metà ci troviamo. In questo esempio, poiché la trama è divisa in 16, o 4 su un lato, questi primi due bit ci dicono in quale pagina siamo. i bit ci dicono la posizione all'interno della pagina.
Possiamo ottenere i bit rimanenti spostando il float con exp2 () e rimuovendoli con fract ()
float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);
Dove numTiles è un int2 che indica il numero di tessere per lato della trama. Nel nostro esempio, questo sarebbe (4, 4)
Quindi calcoliamo inPageLocation per il punto verde, (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);
Un'ultima cosa da fare prima che abbiamo finito. Attualmente, inPageLocation è una coordinata UV nello 'spazio' di trama virtuale. Tuttavia, vogliamo una coordinata UV nello "spazio" della trama fisica. Per fare ciò non ci resta che ridimensionare inPageLocation in base al rapporto tra dimensione della trama virtuale e dimensione della trama fisica
inPageLocation *= physicalTextureSize / virtualTextureSize;
Quindi la funzione finita è:
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;
}