Texturização virtual é o extremo lógico dos atlas de textura.
Um atlas de textura é uma única textura gigante que contém texturas para malhas individuais dentro dele:
Os atlas de textura se tornaram populares devido ao fato de que a alteração de texturas causa um fluxo completo de pipeline na GPU. Ao criar as malhas, os UVs são comprimidos / deslocados para que representem a "porção" correta de todo o atlas de textura.
Como @ nathan-reed mencionado nos comentários, uma das principais desvantagens dos atlas de textura é a perda de modos de quebra automática, como repetição, grampo, borda, etc. Além disso, se as texturas não tiverem bordas suficientes, você poderá acidentalmente amostra de uma textura adjacente ao fazer a filtragem. Isso pode levar a artefatos sangrentos.
Os Atlas de textura têm uma grande limitação: tamanho. As APIs gráficas colocam um limite suave para o tamanho da textura. Dito isto, a memória gráfica é tão grande. Portanto, há também um limite rígido no tamanho da textura, dado pelo tamanho do seu v-ram. Texturas virtuais resolvem esse problema, emprestando conceitos da memória virtual .
As texturas virtuais exploram o fato de que, na maioria das cenas, você vê apenas uma pequena porção de todas as texturas. Portanto, apenas esse subconjunto de texturas precisa estar no vram. O restante pode estar na RAM principal ou no disco.
Existem algumas maneiras de implementá-lo, mas vou explicar a implementação descrita por Sean Barrett em sua palestra no GDC . (que eu recomendo assistir)
Temos três elementos principais: a textura virtual, a textura física e a tabela de pesquisa.
A textura virtual representa o mega atlas teórico que teríamos se tivéssemos vram suficientes para caber em tudo. Na verdade, ele não existe na memória em nenhum lugar. A textura física representa os dados de pixel que realmente temos no vram. A tabela de pesquisa é o mapeamento entre os dois. Por conveniência, dividimos os três elementos em blocos ou páginas de tamanhos iguais.
A tabela de pesquisa armazena a localização do canto superior esquerdo do bloco na textura física. Então, dado um UV para toda a textura virtual, como obtemos o UV correspondente para a textura física?
Primeiro, precisamos encontrar o local da página dentro da textura física. Então precisamos calcular a localização do UV dentro da página. Finalmente, podemos adicionar esses dois deslocamentos para obter a localização do UV dentro da textura física
float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;
Calculando pageLocInPhysicalTex
Se tornarmos a tabela de pesquisa do mesmo tamanho que o número de blocos na textura virtual, podemos apenas amostrar a tabela de pesquisa com a amostragem do vizinho mais próximo e obteremos a localização do canto superior esquerdo da página na textura física.
float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);
Calculando inPageLocation
inPageLocation é uma coordenada UV relativa à parte superior esquerda da página, e não à parte superior esquerda de toda a textura.
Uma maneira de calcular isso é subtraindo a UV da parte superior esquerda da página e depois escalando para o tamanho da página. No entanto, isso é um pouco de matemática. Em vez disso, podemos explorar como o ponto flutuante IEEE é representado. O ponto flutuante IEEE armazena a parte fracionária de um número por uma série de frações de base 2.
Neste exemplo, o número é:
number = 0 + (1/2) + (1/8) + (1/16) = 0.6875
Agora vamos ver uma versão simplificada da textura virtual:
O bit 1/2 indica se estamos na metade esquerda da textura ou na direita. O 1/4 bit indica em que quarto da metade estamos. Neste exemplo, como a textura é dividida em 16 ou 4 em um lado, esses dois primeiros bits nos dizem em que página estamos. bits nos dizem a localização dentro da página.
Podemos obter os bits restantes deslocando o flutuador com exp2 () e removendo-os com fract ()
float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);
Onde numTiles é um int2 fornecendo o número de ladrilhos por lado da textura. No nosso exemplo, isso seria (4, 4)
Então, vamos calcular o inPageLocation para o ponto 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);
Uma última coisa a fazer antes de terminarmos. Atualmente, inPageLocation é uma coordenada UV no 'espaço' da textura virtual. No entanto, queremos uma coordenada UV no 'espaço' da textura física. Para fazer isso, basta escalar inPageLocation pela proporção entre o tamanho da textura virtual e o tamanho da textura física
inPageLocation *= physicalTextureSize / virtualTextureSize;
Portanto, a função final é:
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;
}