É possível construir um cubo com menos de 24 vértices

14

Eu tenho um mundo baseado em cubo como o Minecraft e me pergunto se existe uma maneira de construir um cubo com menos de 24 vértices para reduzir o uso de memória.

Não me parece possível por dois motivos: os normais não saem bem e as texturas por rosto não funcionam.

É este o caso ou estou errado? Talvez haja alguma nova tecnologia DX11 sofisticada que possa ajudar?

Edit: Apenas para esclarecer, tenho 2 requisitos: preciso de normais de superfície para cada face do cubo para obter iluminação adequada e preciso de uma maneira de abordar um índice diferente em uma matriz de textura para cada face do cubo

Telanor
fonte
1
Deseja reduzir o uso de memória da placa gráfica ou de RAM ?
22812 doppelgreener
1
Os dados do vértice são armazenados na RAM e na GPU agora, reduzindo-os reduziria ambos.
Telanor

Respostas:

9

Se você só precisa de normais por face e se seus cabos de texto para uma face são estritamente 0/0, 0/1, 1/0, 1/1 (ou similares para se adequar ao seu layout), você pode construir um cubo com 8 verts e 30 (faixa com reinicialização) ou 36 (lista) índices. Busque os normais e os cabos de texto usando uma pesquisa de matriz constante baseada em SV_VertexID no seu sombreador de vértices.

Fazer isso significa que você nem precisa incluir cabos de texto ou normais no seu buffer de vértice, o que proporcionará ainda mais economia de memória.

Indo mais longe, você ainda pode ir até 24 verts por cubo, mas também usar instanciamento. Cada cubo teria um tamanho fixo no seu buffer de vértice (1x1x1) e você teria um fator de escala e uma posição (assumindo que seus cubos não giram, uma matriz se o fizer) como dados por instância. No caso não rotativo, você tem um custo único de 24 verts, mas cada cubo precisa apenas de 6 flutuadores para especificar completamente. No caso rotativo, você está olhando para 16 carros alegóricos, mas mesmo isso é uma economia substancial (nesse caso, é mais provável que você gargale o lado da CPU nas transformações de matriz - para o caso não rotativo que constrói uma matriz em tempo real seu sombreador de vértice - mesmo que seja feito por vértice, é tão estupidamente rápido que você nem precisa se preocupar com isso).

Para texturas por face, basta usar uma matriz de textura. Você precisa garantir que cada textura na matriz tenha o mesmo tamanho, é claro, e ainda precisará interromper o lote atual se a própria matriz precisar mudar, mas, caso contrário, ele fará o trabalho muito bem. Adicione um terceiro texcoord à sua definição de vértice, que define a fatia da matriz a ser usada para cada face.

Você não precisa de um GS com isso, e ele deve ser executado mais rapidamente do que usar um, pois ter o estágio de sombreador de geometria ativado imporá uma sobrecarga extra por si só.

Eu tenho código de referência no meu mecanismo que apenas desenha um monte de cubos usando esse método, e posso facilmente percorrer mais de 300.000 cubos enquanto ainda limpo 60 fps, em uma GPU relativamente baixa, e sem fazer mais nada para otimizar o processo . É certo que não os estou iluminando nem texturizando, mas tenho a mistura alfa ativada, o descarte da face posterior desativado e, em geral, se equilibra com a parte "não fazendo mais nada para otimizar", portanto, você deve ter uma idéia razoável do tipo de estimativa que você pode acertar com este método.

Maximus Minimus
fonte
1
Eu estava usando instanciamento antes e funciona muito bem para cubos com menos de 40k. Mas eu tenho pelo menos 256k cubos, e instanciar não estava lidando com isso também. Usar SV_VertexID com uma pesquisa parece muito promissor.
Telanor
Qual era o tamanho do seu buffer por instância? Tentar colocá-los todos em um buffer enorme pode acabar sufocando sua GPU; Geralmente, mantenho os buffers por instância em espaço suficiente para objetos de 64k (dividindo as chamadas de desenho, se necessário), uso o padrão de descarte / sem substituição e escrevo diretamente no ponteiro mapeado em vez de copiar de um array intermediário, que funciona muito bem .
Maximus Minimus
Além disso - manter o número de mapas baixo é essencial. Mapear / gravar um cubo / remover o mapa 40k vezes será muito mais lento que mapear / gravar 40k cubos / remover o mapeamento apenas uma vez. Se você estava fazendo o primeiro, tente mudar para o último.
Maximus Minimus
15

Eu acho que a principal otimização que você pode fazer é baseada no fato de que nem todo cubo precisará de todos os 24 vértices . De fato, os únicos cubos que precisam de 24 vértices são aqueles que flutuam no ar, o que provavelmente é uma ocorrência rara.

Em geral, gere apenas quads para os rostos que estão em contato com o ar . Isso significa que, se dois cubos estão se tocando, você não precisa gerar nenhum vértice para a face em que eles estão tocando.

A imagem abaixo demonstra esse conceito, mas em 2D para facilitar a compreensão. Na imagem, existem 11 blocos ocupados (representados pelos círculos cinzentos preenchidos), que normalmente exigiriam 4 x 11 = 44 arestas para representar (4 porque é um quadrado, não um cubo). Mas, como você pode ver, você realmente só precisa desenhar as arestas quando estiver em contato com um quadrado vazio, que neste caso é de apenas 8 arestas.

insira a descrição da imagem aqui

Se um exemplo tão simples em 2D conseguiu reduzir 44 arestas para 8 arestas, imagine os ganhos em um grande mundo 3D ...

Essa é a abordagem descrita neste artigo , que eu recomendo que você leia, apesar de ter como objetivo o OpenGL. Os conceitos devem ser bastante universais.

Você também pode provavelmente usar um sombreador de geometria para gerar os vértices em tempo real na GPU, eliminando a necessidade de armazená-los na memória, mas não tenho experiência com isso, nem sei quão bem ele funcionaria para uma grande mundo.

David Gouveia
fonte
1
Artigo muito interessante. Já estou fazendo a otimização de borda, o que certamente ajuda bastante. Vou tentar os shaders de geometria e ver se isso funciona.
Telanor 26/06/12
Quer dizer, use o sombreador de geometria e o produto cruzado para calcular as normais? Eu estava pensando em usá-lo, como criar um programa separado para superfícies planas (não estou preocupado com texturas).
dreta
@dreta Não apenas isso, mas indo até o ponto em que você envia apenas os centróides do cubo como uma lista de pontos, e todos os vértices são emitidos pelo GS. Talvez codifique algumas informações vizinhas nos pontos para que o sombreador gere apenas os rostos realmente necessários, embora eu não tenha pensado em como.
David Gouveia
Se você quer desempenho, não use um sombreador de geometria ... Quase sempre. Eles estragam completamente o oleoduto. Se você deseja gerar geometria na GPU, usar um sombreador de computação ou OpenCL
Robert Fraser