Estou escrevendo um jogo OpenGL 3D. Haverá toneladas de triângulos para terrenos e objetos em uso.
Estou estudando no guia oficial do OpenGL e o primeiro método apresentado é chamar uma função glVertex
após o glBegin
para cada vértice que você deseja desenhar.
No entanto, esse método parece bastante antigo e ineficiente quando você precisa desenhar milhares de triângulos. Eu acho que existem métodos para armazenar na memória da placa gráfica as informações que não mudam em todos os quadros, de modo que, quando você renderiza cada quadro, essas informações já estão lá. Enquanto, para obter mais informações "instáveis", você provavelmente precisará enviar novos dados de vértice a cada quadro e, portanto, uma técnica diferente poderá ser mais eficiente.
Voltando à minha pergunta: eu gostaria de uma explicação clara das funções e técnicas OpenGL mais modernas e eficientes usadas para enviar informações de vértices ao hardware e dar instruções para renderizar.
Quais são as melhores maneiras e ferramentas OpenGL de enviar a ordem para renderizar dados de vértices que raramente mudam durante os quadros (penso em terrenos e objetos) e qual a melhor maneira de enviar dados de vértices que mudam bastante durante os quadros?
fonte
Respostas:
Em termos gerais, a resposta é "depende". A maneira de enviar atualizações de partículas é um pouco diferente da maneira como você envia uma série de modelos de GPU.
Buffers de vértice / índice
No sentido geral, porém, tudo hoje em dia é feito com um VBO ( objeto de buffer de vértice ). A antiga API de modo imediato (
glBegin
/glEnd
) provavelmente é apenas implementada como um invólucro de gordura em torno do sistema de buffer de vértice interno do driver.Para objetos estáticos, crie um VBO estático e preencha-o com seus dados de vértice. Se algum dos vértices for compartilhado (geralmente o caso), você provavelmente também desejará criar um buffer de índice. Isso reduz a necessidade de enviar os mesmos dados mais de uma vez para alterar malhas (potencialmente economizando na largura de banda de transferência) e no processamento (economia no tempo de sombreamento do vértice). Observe que você desenha com funções diferentes ao fazer desenhos indexados ou não indexados.
Para objetos dinâmicos, faça o mesmo, embora com um conjunto dinâmico de buffers.
Notas Avançadas
Para pedaços maiores, como terrenos, você provavelmente não quebrará a malha em vários pedaços. Fazer a GPU renderizar cem milhões de triângulos quando apenas duzentos mil deles são visíveis é um grande desperdício, especialmente se não forem classificados e houver muitas invocações de overdraw e desperdício de shader de fragmento. Divida a malha em pedaços grandes e, em seguida, processe apenas aqueles que estão dentro da visão frustrante. Existem também várias técnicas de descarte mais avançadas que você pode usar para eliminar pedaços que podem ser frustrantes, mas que estão inteiramente atrás de uma colina, prédio ou algo assim. Manter a contagem decrescente de chamadas é boa, mas há um equilíbrio (que você precisa encontrar para seu aplicativo / hardware específico) entre minimizar chamadas de chamadas e minimizar o desenho da geometria oculta.
Uma das principais coisas a ter em mente com um buffer da GPU é que você não pode gravar nele enquanto a GPU está lendo. Você precisa informar ao motorista que não há problema em descartar a cópia antiga do buffer (quando estiver pronto) e fornecer uma nova (se o antigo estiver ocupado). É claro que, por um longo tempo, não havia função do OpenGL para fazer isso (agora há InvalidateBufferData for GL 4.3 e algumas implementações mais antigas como uma extensão). Em vez disso, existe um comportamento não padrão, mas comum, que a maioria dos drivers implementa. Faça isso para descartar o buffer antes de atualizá-lo:
É claro que mude
GL_ARRAY_BUFFER
eGL_DYNAMIC_DRAW
para os valores apropriados para o buffer. Os buffers estáticos não serão atualizados (ou não deveriam ser), portanto é improvável que você precise se preocupar com o descarte de um buffer.Observe que pode ser mais rápido usar
glBufferData
ouglBufferSubData
ou mais rápidoglMapBuffer
. Realmente depende do driver e do hardware. A geração atual do hardware do PC provavelmente será mais rápida,glBufferData
mas com certeza testará.Outra técnica é usar instanciamento . A instância permite fazer uma única chamada de desenho que desenha várias cópias dos dados em um buffer de vértice / índice. Se você tivesse, digamos, 100 pedras idênticas, convém desenhá-las de uma só vez, em vez de fazer 100 empates independentes.
Ao instanciar, você precisa colocar os dados por instância em outro buffer (como a posição do objeto de cada indivíduo). Pode ser um buffer uniforme ( buffer constante na terminologia do D3D) ou um buffer de textura ou um atributo de vértice por instância. Novamente, o que é mais rápido depende. Os atributos por instância são provavelmente mais rápidos e definitivamente muito mais fáceis de usar, mas muitas implementações GL comuns ainda não suportam,
glBindingAttribDivisor
portanto você terá que ver se está disponível para uso e se é realmente mais rápido (alguns drivers mais antigos emularam instanciando anexando buffers e acabou sendo mais lento usar instanciamentos neles do que fazer chamadas independentes e não há uma maneira padrão de descobrir ... as alegrias de usar o OpenGL).Também existem algoritmos para a otimização do cache de vértices , que é o ato de ordenar vértices / índices em seus buffers para reproduzirem com o cache de vértices nas GPUs modernas. Uma GPU executa apenas o sombreador para um vértice e o armazena em cache no cache de vértices, mas pode ter que ser despejado muito cedo para liberar espaço para outros vértices. (Digamos, dois triângulos compartilham um vértice, mas existem outros 100 triângulos desenhados entre eles; o vértice compartilhado provavelmente acabará sendo desperdiçado por duas vezes pelo shader de vértice.)
Alguns desses recursos requerem uma versão suficientemente nova do GL ou GLES. O GLES2 não suportava instâncias, por exemplo.
Sempre perfil
Novamente, se você se preocupa com o desempenho, teste cada método possível e veja qual é mais rápido para o seu aplicativo no hardware de destino. Não apenas diferentes hardwares / drivers de diferentes fabricantes serão diferentes, mas algumas classes inteiras de hardware são naturalmente diferentes. Uma GPU móvel típica é uma fera muito diferente de uma GPU de desktop discreta típica. Técnicas que são "melhores" em um não serão necessariamente melhores em outro.
Quando se trata de desempenho, seja sempre cético .
fonte