La textura virtual es el extremo lógico de los atlas de texturas.
Un atlas de texturas es una única textura gigante que contiene texturas para mallas individuales en su interior:
Los atlas de textura se hicieron populares debido al hecho de que el cambio de texturas provoca un enjuague completo de la tubería en la GPU. Al crear las mallas, los UV se comprimen / desplazan para que representen la 'porción' correcta del atlas de texturas completo.
Como @ nathan-reed mencionó en los comentarios, uno de los principales inconvenientes de los atlas de texturas es perder modos de ajuste como repetir, pinzar, borde, etc. Además, si las texturas no tienen suficiente borde alrededor, puede accidentalmente muestra de una textura adyacente al hacer el filtrado. Esto puede provocar artefactos sangrantes.
Los Atlas de textura tienen una limitación importante: el tamaño. Las API de gráficos establecen un límite suave sobre cuán grande puede ser una textura. Dicho esto, la memoria gráfica es tan grande. Por lo tanto, también hay un límite estricto en el tamaño de la textura, dado por el tamaño de su v-ram. Las texturas virtuales resuelven este problema, tomando prestados conceptos de la memoria virtual .
Las texturas virtuales explotan el hecho de que en la mayoría de las escenas, solo se ve una pequeña porción de todas las texturas. Entonces, solo ese subconjunto de texturas debe estar en vram. El resto puede estar en la RAM principal o en el disco.
Hay algunas formas de implementarlo, pero explicaré la implementación descrita por Sean Barrett en su charla de GDC . (que recomiendo ver)
Tenemos tres elementos principales: la textura virtual, la textura física y la tabla de búsqueda.
La textura virtual representa el mega atlas teórico que tendríamos si tuviéramos suficiente vram para todo. En realidad no existe en la memoria en ninguna parte. La textura física representa los datos de píxeles que realmente tenemos en vram. La tabla de búsqueda es el mapeo entre los dos. Para mayor comodidad, dividimos los tres elementos en mosaicos o páginas de igual tamaño.
La tabla de búsqueda almacena la ubicación de la esquina superior izquierda del mosaico en la textura física. Entonces, dado un UV a toda la textura virtual, ¿cómo obtenemos el UV correspondiente para la textura física?
Primero, necesitamos encontrar la ubicación de la página dentro de la textura física. Luego necesitamos calcular la ubicación de la radiación UV dentro de la página. Finalmente, podemos agregar estos dos desplazamientos para obtener la ubicación de los rayos UV dentro de la textura física.
float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;
Calculando pageLocInPhysicalTex
Si hacemos que la tabla de búsqueda tenga el mismo tamaño que el número de mosaicos en la textura virtual, solo podemos muestrear la tabla de búsqueda con el muestreo del vecino más cercano y obtendremos la ubicación de la esquina superior izquierda de la página dentro de la textura física.
float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);
Cálculo en la ubicación de la página
inPageLocation es una coordenada UV que es relativa a la esquina superior izquierda de la página, en lugar de a la esquina superior izquierda de toda la textura.
Una forma de calcular esto es restando el UV de la esquina superior izquierda de la página y luego escalando al tamaño de la página. Sin embargo, esto es bastante matemático. En cambio, podemos explotar cómo se representa el punto flotante IEEE. El punto flotante IEEE almacena la parte fraccionaria de un número por una serie de fracciones de base 2.
En este ejemplo, el número es:
number = 0 + (1/2) + (1/8) + (1/16) = 0.6875
Ahora veamos una versión simplificada de la textura virtual:
El 1/2 bit nos dice si estamos en la mitad izquierda de la textura o en la derecha. El cuarto bit nos dice en qué cuarto de la mitad estamos. En este ejemplo, dado que la textura se divide en 16, o 4 a un lado, estos dos primeros bits nos dicen en qué página estamos. El resto los bits nos dicen la ubicación dentro de la página.
Podemos obtener los bits restantes cambiando el flotador con exp2 () y eliminándolos con fract ()
float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);
Donde numTiles es un int2 que da el número de mosaicos por lado de la textura. En nuestro ejemplo, esto sería (4, 4)
Entonces, calculemos inPageLocation para el 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);
Una última cosa que hacer antes de que hayamos terminado. Actualmente, inPageLocation es una coordenada UV en la textura virtual 'espacio'. Sin embargo, queremos una coordenada UV en la textura física 'espacio'. Para hacer esto, solo tenemos que escalar inPageLocation por la proporción del tamaño de textura virtual al tamaño de textura física
inPageLocation *= physicalTextureSize / virtualTextureSize;
Entonces la función terminada es:
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;
}