Para renderização de voxel, o que é mais eficiente: VBO pré-fabricado ou um sombreador de geometria?

26

Dada uma matriz de voxel razoavelmente estática, o que é mais eficiente: usar a CPU para gerar previamente um VBO para renderizar as faces de voxel (ignorando formas mais avançadas de renderização como cubos em marcha por enquanto) ou usar um sombreador de geometria na GPU para gerar o rostos na mosca?

Não estou preocupado com a atualização de voxels alterados, mas é claro que isso é um benefício da versão GPU, pois você não precisa reconstruir os VBOs. Além disso, a abordagem GS parece um pouco mais moderna :)

Por outro lado, não observei os detalhes de como um GS realmente funciona com o pipeline de rasterização nas GPUs modernas. Ele gera os vértices em uma espécie de cache de fluxo ou os vértices são gravados na memória normal da GPU no meio? Se for o último, a geração on-the-fly pode reduzir a largura de banda disponível e o poder de processamento do restante das tarefas da GPU, eu acho, e seria mais benéfico fazê-lo na CPU.

Bjorn Wesen
fonte

Respostas:

9

Estou pensando em uma cena do tipo minecraft, onde por voxel você quer dizer um mundo de blocos que são realmente renderizados usando polígonos:

Se você usar um sombreador de geometria, será difícil evitar ter exatamente três faces (ou o que for) por voxel.

Se você tiver muitos blocos adjacentes com a mesma textura, poderá usar o mosaico das texturas para ter muito menos triângulos na sua faixa (degenerada) em uma abordagem de VBO. Quero dizer, se houver uma área grande e plana 6x6 de voxels de grama, você pode desenhar o topo inteiro em apenas 2 triângulos em vez de 64.

Com a abordagem GS, você também não pode fazer o descarte trivial de faces ocluídas por voxels adjacentes, o que é muito direto com a abordagem VBO.

Eu não tentei a abordagem GS, mas posso dizer que a abordagem VBO com a combinação de repetição de blocos adjacentes funciona muito bem. Achei que mexer com os índices de elementos era muito mais lento do que apenas repetir os vértices. Se você dividir seu mundo em pequenos cubos, normalmente poderá usar apenas um byte por componente por vértice e até empacotar as informações de textura e os normais (uma face em um cubo alinhado ao eixo tem apenas três normais possíveis) etc. em um quarto byte para criar 4 bytes por vértice, o que é agradável e rápido.

Eu usei VBOs separados para cada uma das 6 faces - você só precisa desenhar no máximo 3 delas, obviamente. Isso se encaixa perfeitamente com as diferentes texturas normalmente usadas nas partes superiores dos voxels no estilo minecraft. Porque para cada conjunto o normal e tal são então uniformes.

Com o uso de pixmaps de mosaico vertical em um atlas GL_REPEATno eixo horizontal e com versões rotacionadas em 90 graus dos pixmaps no mesmo atlas, descobri que posso desenhar grandes quantidades de blocos aparentemente diferentes usando o mesmo VBO na mesma chamada. No exemplo da área de grama 6x6, eu o dividiria em 12 triângulos, pois só tenho repetição em uma dimensão no meu atlas.

Eu tenho conseguido que isso funcione na extremidade mais baixa de chips gráficos integrados e móveis, onde GS é apenas algo que eu posso sonhar um dia brincando.

Vai
fonte
3
Você só precisa desenhar no máximo 3 faces por voxel, mas pode precisar desenhar faces diferentes para cada voxel, dependendo do ponto de vista, para que a otimização não seja tão fácil, certo? Um VBO pré-fabricado conterá mais de um voxel. Se o seu ponto de vista estiver entre os voxels, você verá o lado leste de um e o lado oeste do outro. A única maneira de ajudar é que você pode separar trivialmente as faces de trás para trás, mas o pior caso ainda é renderizar 5 de 6 lados em um grupo de voxels. Se o seu ponto de vista estiver fora dos limites axiais do VBO, você precisará renderizar apenas 3 lados.
Bjorn Wesen
Local em Bjorn, é factível. (Mas eu estou criando VBOs para blocos como necessário e reconsiderar o que eu construí quando a câmera se move, em vez de ter o mundo inteiro em VBOs em todos os momentos; então eu tenho um tempo natural para fazer essas escolhas)
Will
10

E a terceira opção, usando matrizes instanciadas? Basicamente, você desenha muitas caixas (feitas de um cubo simples de 8 vértices) com uma única chamada de desenho, fornecendo as posições (e outros dados) como atributos por instância do VBO voxel-data (usando glVertexAttribDivisorno OpenGL, tenho certeza DX tem isso também). Isso pode ser mais rápido do que a abordagem de sombreador de geometria, embora o código do aplicativo (sem sombreador) deva ser bastante semelhante, pois eu me lembro de sombreadores de geometria com reputação de serem lentos, embora eu não tenha experiência com eles (ou instanciação) enquanto ainda estou sentado no hardware 2.1.

Mas, de qualquer forma, shaders de geometria ou matrizes instanciadas devem ser mais adequadas que a geometria de voxel criada por CPU, especialmente quando os dados de voxel estão sujeitos a alterações. Em conjunto com o feedback de transformação (saída de fluxo no DX?), Você pode configurar uma boa técnica de seleção baseada em GPU.

Chris diz Restabelecer Monica
fonte
Sim, esta é a melhor solução para este problema. Por que não me ocorreu? :)
Notabene
Depois de algumas experiências, tenho de lhe dizer que a geometria assada supera qualquer instancia por uma ampla margem. Ainda não tentei shaders de geometria.
Jari Komppa
@JariKomppa, você pode elaborar o que você quer dizer com geometria assada?
9605 Steven St Luff
Instâncias pré-traduzidas e copiadas para uma única malha. Como ter uma malha que represente cem cubos ou qualquer outra coisa.
Jari Komppa
@JariKomppa Eu vi os mesmos resultados, onde a criação da malha é muito mais rápida. No entanto, no gtx 680, a opção de instanciamento parece funcionar muito mais rápido, mais estranho.
Levi H
1

A versão de sombreador de geometria me parece muito melhor. Você pode ter apenas o ponto vbo e a caixa de construção em tempo real (ponto de entrada, fluxo do triângulo de saída). Será rápido (ainda mais rápido se você usar a unidade de mosaico no shader model 5 eq. DX11) e reduzirá extremamente a largura de banda, será uma solução agradável e limpa.

Sobre a GS. Ele é colocado entre o vertex shader e o pixel shader e modifica o fluxo de vértice emitido (primitivos). Enquanto o sombreador de vértice funciona apenas nos vértices, o sombreador de geometria funciona em primitivas inteiras. A saída desse fluxo vai apenas para o pixel shader (e é rasterizada antes disso naturalmente :)) e não há como salvá-lo. (Talvez por uma renderização maluca para texturizar e depois analisá-la ... mas não há nenhuma possibilidade real)

Nota de desempenho: você deve conseguir tudo no sombreador de geometria e pular (apenas passar dados) o sombreamento de vértice. Mas não é o melhor caminho. Melhor (mais rápido) é fazer a maior parte da transformação possível no sombreador de vértice e tentar minimizar o programa de sombreamento de geometria. Não tenha medo de usar o ciclo se precisar (para criação de caixas, por exemplo). O compilador desenrolará para você.

Notabene
fonte
2
Pode ser uma boa ideia verificar se há voxels adjacentes na geometria e / ou sombreamento de vértice e descartar os vértices ou pular as faces se estiverem obstruídas. Caso contrário, a solução GS aumentará a largura de banda usada.
Tamschi 12/09
A largura de banda não será um grande problema (pelas minhas experiências), mas é claro que é verdade. E você não pode procurar nas outras primitivas no GS (é que eu sei :)).
Notabene
@ Tamschi: sim, esse problema me ocorreu logo após escrever esta pergunta. Para a versão da CPU, os voxels no meio dos sólidos são suprimidos, mas isso pode ser impossível na GPU sem um pré-passe com o que equivaleria a um differencing ..
Bjorn Wesen
11
Você pode vincular o buffer de vértice a um uniforme isamplerBuffer ou usarplerBuffer no shader e fazer pesquisas com textura (nome_de_uniforme, índice). Outra opção seria ligar o buffer a uma matriz uniforme, o que lhe dá mais liberdade em qual formato de vértice você deseja usar.
Tamschi 13/09/11