Como a texturização virtual pode realmente ser eficiente?

27

Para referência, estou me referindo ao "nome genérico" da técnica primeiro (acredito) introduzida com a tecnologia MegaTexture da idTech 5 . Veja o vídeo aqui para ver rapidamente como funciona.

Ultimamente, venho pesquisando alguns artigos e publicações relacionados a ele, e o que não entendo é como isso pode ser eficiente. Não requer recálculo constante das coordenadas UV do espaço "página de textura global" para as coordenadas virtuais de textura? E como isso não limita a maioria das tentativas de agrupar geometria por completo? Como pode permitir o zoom arbitrário? Em algum momento, não seria necessário subdividir polígonos?

Há tanta coisa que eu não entendo e não consegui encontrar recursos realmente facilmente acessíveis sobre o assunto.

Llamageddon
fonte

Respostas:

20

visão global

O principal motivo para a Virtual Texturing (VT), ou Sparse Virtual Textures , como às vezes é chamado, é como uma otimização de memória. O essencial é apenas mover para a memória de vídeo os texels reais (generalizados como páginas / blocos) que você pode precisar para um quadro renderizado. Assim, você terá muito mais dados de textura no armazenamento offline ou lento (HDD, disco óptico, nuvem) do que caberia na memória de vídeo ou até na memória principal. Se você entende o conceito de memória virtual usado pelos sistemas operacionais modernos, é a mesma coisa em sua essência (o nome não é dado por acidente).

O VT não requer a recálculo dos UVs no sentido de que você faria isso em cada quadro antes de renderizar uma malha e, em seguida, reenviaria os dados de vértice, mas requer um trabalho substancial nos shaders Vertex e Fragment para executar a pesquisa indireta dos UVs recebidos. Em uma boa implementação, no entanto, deve ser completamente transparente para o aplicativo se estiver usando uma textura virtual ou tradicional. Na verdade, na maioria das vezes, um aplicativo combina os dois tipos de texturização, virtual e tradicional.

Teoria de lotes pode, em teoria, funcionar muito bem, embora eu nunca tenha examinado os detalhes disso. Como os critérios habituais para o agrupamento da geometria são as texturas, e com o VT, todos os polígonos da cena podem compartilhar a mesma textura "infinitamente grande", teoricamente, você poderia obter um desenho completo da cena com uma chamada de desenho. Mas, na realidade, outros fatores entram em jogo, tornando isso impraticável.

Problemas com o VT

Ampliar / reduzir e movimento abrupto da câmera são as coisas mais difíceis de lidar em uma configuração de TV. Pode parecer muito atraente para uma cena estática, mas assim que as coisas começarem a se mover, serão solicitadas mais páginas / blocos de textura do que você pode transmitir para armazenamento externo. As entradas e saídas do arquivo assíncrono e a segmentação podem ajudar, mas se for um sistema em tempo real, como em um jogo, você precisará renderizar alguns quadros com blocos de menor resolução até que os de alta resolução cheguem, de vez em quando , resultando em uma textura embaçada. Não há bala de prata aqui e esse é o maior problema com a técnica, IMO.

A Texturização virtual também não lida com transparência de maneira fácil; portanto, polígonos transparentes precisam de um caminho de renderização tradicional separado para eles.

Em suma, o VT é interessante, mas eu não o recomendaria para todos. Pode funcionar bem, mas é difícil de implementar e otimizar, além de haver muitos casos de canto e ajustes específicos de caso necessários para o meu gosto. Mas, para grandes jogos de mundo aberto ou aplicativos de visualização de dados, pode ser a única abordagem possível para ajustar todo o conteúdo ao hardware disponível. Com muito trabalho, ele pode ser executado com bastante eficiência, mesmo em hardware limitado, como podemos ver nas versões PS3 e XBOX360 do Rage da id .

Implementação

Consegui fazer o VT funcionar no iOS com o OpenGL-ES, até certo ponto. Minha implementação não é "entregável", mas eu poderia fazê-lo se quisesse e tivesse os recursos. Você pode ver o código-fonte aqui , pode ajudar a ter uma idéia melhor de como as peças se encaixam. Aqui está um vídeo de uma demonstração em execução no iOS Sim. Parece muito lento porque o simulador é péssimo para emular shaders, mas roda sem problemas em um dispositivo.

O diagrama a seguir descreve os principais componentes do sistema em minha implementação. Difere bastante da demonstração SVT de Sean (link abaixo), mas está mais próxima da arquitetura do apresentado pelo artigo Acelerando a Texturização Virtual Usando CUDA , encontrado no primeiro livro do GPU Pro (link abaixo).

sistema de texturização virtual

  • Page Filessão as texturas virtuais, já cortadas em blocos (páginas AKA) como uma etapa de pré-processamento, para que estejam prontas para serem movidas do disco para a memória de vídeo sempre que necessário. Um arquivo de paginação também contém todo o conjunto de mipmaps, também chamados de mipmaps virtuais .

  • Page Cache Managermantém uma representação do lado do aplicativo Page Tablee das Page Indirectiontexturas. Como mover uma página do armazenamento offline para a memória é caro, precisamos de um cache para evitar recarregar o que já está disponível. Esse cache é um cache LRU ( Menos Utilizado Recentemente) muito simples . O cache também é o componente responsável por manter as texturas físicas atualizadas com sua própria representação local dos dados.

  • A Page Provideré uma fila de trabalhos assíncrona que vai buscar as páginas necessárias para uma determinada visão da cena e enviá-los para o cache.

  • A Page Indirectiontextura é uma textura com um pixel para cada página / bloco na textura virtual, que mapeará os UVs recebidos para a Page Tabletextura do cache que possui os dados texel reais. Essa textura pode ficar muito grande, por isso deve usar um formato compacto, como RGBA 8: 8: 8: 8 ou RGB 5: 6: 5.

Mas ainda falta uma peça-chave aqui, e é assim que se determina quais páginas devem ser carregadas do armazenamento no cache e, consequentemente, no Page Table. É aí que o Feedback Pass e o Page Resolverenter.

O Feedback Pass é uma pré-renderização da exibição, com um sombreador personalizado e com uma resolução muito mais baixa, que gravará os IDs das páginas necessárias no buffer de estrutura de cores. Essa colcha de retalhos colorida do cubo e da esfera acima são índices de páginas reais codificados como uma cor RGBA. Essa renderização antes da passagem é então lida na memória principal e processada pelo Page Resolverpara decodificar os índices da página e disparar os novos pedidos com o Page Provider.

Após o pré-passe de Feedback, a cena pode ser renderizada normalmente com os shaders de pesquisa do VT. Mas observe que não esperamos que a nova solicitação de página termine, isso seria terrível, porque simplesmente bloquearíamos a E / S de arquivo síncrono. Os pedidos são assíncronos e podem ou não estar prontos no momento em que a visualização final é renderizada. Se eles estiverem prontos, doce, mas se não estiver, sempre manteremos uma página bloqueada de um mipmap de baixa resolução no cache como fallback, portanto, teremos alguns dados de textura para usar, mas ficará embaçado.

Outros recursos que valem a pena conferir

O VT ainda é um tópico bastante interessante sobre computação gráfica; portanto, há toneladas de bom material disponível; você poderá encontrar muito mais. Se houver mais alguma coisa que eu possa adicionar a esta resposta, não hesite em perguntar. Estou um pouco enferrujado sobre o assunto, não li muito sobre isso no ano passado, mas é sempre bom que a memória revisite as coisas :)

glampert
fonte
Ei, obrigado pela excelente resposta. Sei que isso geralmente é desaprovado, mas tenho vários problemas, então, na maioria das vezes, passo por tudo - para obter uma visão geral intuitiva dos tópicos para o futuro (receio que aprender e implementar corretamente as coisas esteja fora do meu alcance no momento. ) - de qualquer maneira, se possível, você poderia postar um exemplo de pseudocódigo descrevendo o próprio processo, idealmente, mas não necessariamente, ilustrado?
Llamageddon
11
@Llamageddon, acontece que eu ainda tinha um diagrama em mãos;) Receio que o pseudo-código seja um pouco difícil de fornecer, já que há bastante código real nele. Mas espero que a resposta expandida ajude a dar uma idéia geral da técnica.
glampert
3
Vale ressaltar que o hardware mais moderno agora expõe tabelas de páginas programáveis, eliminando a necessidade de uma textura de redirecionamento. Esta é exposta através por exemplo directx12 recursos reservados , que se baseia em DirectX11 azulejos recursos , ou OpenGL texturas esparsas .
MooseBoys
11
@Llamageddon, o pré-passe de feedback pode ser feito em uma resolução mais baixa para economizar o máximo de computação e memória possível, já que os pixels de uma página geralmente se repetem (você pode observar os grandes quadrados coloridos na minha demonstração). Você está certo de que pode acabar perdendo uma página visível como essa, mas isso geralmente não terá um grande impacto visual, porque o sistema sempre deve manter pelo menos o menor mapa mip de todo o VT disponível no cache. No segundo artigo que vinculei, temos todos os exemplos de sombreadores no apêndice. Você também pode consultar o repositório do meu próprio projeto, eles são semelhantes.
Glampert
11
@ glampert Ahh, entendo; isso faz sentido. Ainda assim, acho que existem muitas opções para lidar com transparências; na passagem do ID da página, você pode pontilhar (para que a histograma veja todas as páginas, a menos que haja um grande número de camadas transparentes) ou use uma abordagem de buffer k , ou apenas baseie a residência de textura transparente na qual os objetos estão próximos ao câmera (em vez de renderizá-las em um passe de feedback).
Nathan Reed
11

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:

Exemplo de Atlas de textura

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.

Textura virtual

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.

insira a descrição da imagem aqui

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:

Textura virtual simples

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;
}
RichieSams
fonte
Não estou, estou me referindo à texturização virtual, mais conhecida como a tecnologia MegaTexture da idTech 5 . Veja também isto e isto . Eu o vi mencionado na visão geral de muitos pipelines de renderização de muitos motores modernos e em alguns artigos que usam uma abordagem semelhante para mapas de sombra. Ele tem muito em comum com atlas de textura, sim, ele os usa de certa forma, mas não estou confundindo com atlas de textura.
Llamageddon
Ahh Obrigado pelos links. Você pode adicioná-los à pergunta. Atualizarei minha resposta de acordo.
RichieSams 2/15/15
3
Na IMO, a principal desvantagem dos atlas de textura simples (não texturas virtuais) é que você perde modos de quebra, como repetir e prender, e o sangramento ocorre devido à filtragem / mipmapping - e não à precisão do ponto flutuante. Eu ficaria surpreso ao ver a precisão de flutuação se tornar um problema para texturas comuns (não virtuais); mesmo uma textura de 16K (o máximo permitido pelas APIs atuais) não é grande o suficiente para forçar a precisão da flutuação.
Nathan Reed
@ RichieSams Btw, acho que sua resposta é boa, mesmo que com uma pergunta diferente. Você deve fazer uma postagem de perguntas e respostas.
Llamageddon
Hmm, isso explica muito bem, embora eu realmente não entenda como isso funciona com os níveis mip. Eu gostaria de poder escrever o meu problema específico com o entendimento ela para baixo, mas ele meio que me escapa ...
Llamageddon