Qual é o melhor método para atualizar uniformes de shader?

10

Qual é a maneira mais aceita de manter as matrizes de um shader atualizadas e por quê?

Por exemplo, no momento, tenho uma Shaderclasse que armazena as alças no programa de sombreamento GLSL e uniformes. Toda vez que movo a câmera, tenho que passar a nova matriz de vistas para o shader, então cada objeto do mundo diferente devo passar sua matriz de modelos para o shader.

Isso me limita severamente, pois não posso fazer nada sem ter acesso a esse objeto shader.

Pensei em criar uma ShaderManagerclasse singleton responsável por manter todos os shaders ativos. Posso acessar isso de qualquer lugar e um objeto do mundo não precisaria saber sobre quais shaders estão ativos, apenas que ele precisa informar as ShaderManagermatrizes desejadas, mas não tenho certeza se esse é o melhor caminho e provavelmente há alguns problemas que surgirá da adoção dessa abordagem.

Lerp
fonte
Por que não passar os dados para os sombreadores quando é hora de renderizar?
precisa saber é o seguinte
É isso que pretendo, mas como? Se um objeto cria um sombreador, como os objetos do mundo devem saber sobre a existência desse sombreador e passar as matrizes necessárias?
Lerp

Respostas:

11

Use buffers uniformes (ou seja, buffers constantes no jargão D3D).

Desde que todos os seus sombreadores concordem com o layout e o ponto de ligação de cada buffer, a atualização se tornará fácil. Os modelos não precisam saber nada sobre shaders. Eles precisam apenas atualizar a matriz de exibição de modelo em seu buffer constante e o pipeline de renderização a utilizará automaticamente.

No mínimo, eu argumentaria que você deveria ter dois desses buffers. O primeiro deve armazenar sua matriz de projeção, matriz de câmera, matriz de projeção de câmera concatenada, informações de viewport, detalhes de frustrum e inversos de matriz. Você só precisa atualizar esse buffer uma vez por cena.

Em seguida, forneça a cada modelo outro buffer para armazenar suas propriedades de matriz de exibição de modelo, matriz normal, inversas e material. Isso é atualizado uma vez para cada modelo e pode ser feito em uma passagem de atualização diferente (se / quando apropriado). As informações do material podem / devem ser movidas para um terceiro buffer específico do material, se você puder compartilhar materiais entre vários objetos.

Em uma configuração de sombreamento para frente, faz sentido colocar todas as luzes em outro buffer e, no sombreado diferido, também faz sentido usar um buffer por luz para o passe de luz (no lugar de um modelo / buffer de material usado no passagem de geometria).

Observe que você precisa de uma versão moderadamente atualizada do GL para usar buffers uniformes (3.1 ou uma extensão; bastante comum hoje, exceto em alguns laptops mais antigos, mas ainda em serviço) e precisa de uma versão bastante recente capaz de vincular buffers uniformes a locais de ligação específicos de dentro do código de sombreador (4.2; ainda incomum, mas melhorando), caso contrário, você precisará trabalhar mais no código do lado da CPU para configurar as coisas (o código da CPU precisa saber a ligação correta de qualquer maneira, por isso é mais um cheiro de API do que um problema sério). O OpenGL | ES não adicionou buffers uniformes até o 3.0, que ainda não é suportado nas plataformas móveis mais populares, infelizmente.

Se os buffers não forem uma opção, você precisará de um local global para armazenar locais de índice para o shader ativo. Você pode usar glGetUniformLocationapós carregar o shader para encontrar os índices para nomes conhecidos (como ModelViewMatrixou não) e depois armazená-los. Sua renderização pode mapear valores de enumeração como MODEL_VIEWpassados ​​para uma SetUniformfunção de invólucro para examinar o sombreador associado, localizar o índice e chamar glUniformadequadamente. Não é uma grande mudança específica no uso do código do cliente de buffers, além da necessidade de definir cada uniforme individualmente, se você concluir tudo bem.

Consulte Blocos de interface GLSL e objetos de buffer uniformes .

Sean Middleditch
fonte
Eu sabia que o seu seria uma maneira embutida. Obrigado!
9608 Lerp
4

A maneira mais simples de fazer isso é padronizar apenas nomes uniformes entre seus shaders e enviar todos os dados antes de desenhar. A sobrecarga não é insana, mas você pode otimizá-la mais tarde para nem sempre enviar uniformes menos atualizados.

A maneira como faço isso no meu jogo é que tenho uma Materialclasse abstrata . Os materiais agem como uma ponte entre o jogo e os shaders. Cada Materialum tem uma Shadere várias propriedades que podem ser definidas, incluindo texturas. Quando é hora de desenhar um objeto, ele Materialé vinculado. O Bindmétodo possui um GraphicsStateparâmetro que contém todo o estado gráfico atual - matrizes, luzes etc. O Bindmétodo vincula o shader e as texturas e define todos os uniformes, usando o que for necessário GraphicsState.

Robert Rouhani
fonte