Qual é a maneira mais aceita de manter as matrizes de um shader atualizadas e por quê?
Por exemplo, no momento, tenho uma Shader
classe 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 ShaderManager
classe 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 ShaderManager
matrizes desejadas, mas não tenho certeza se esse é o melhor caminho e provavelmente há alguns problemas que surgirá da adoção dessa abordagem.
Respostas:
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
glGetUniformLocation
após carregar o shader para encontrar os índices para nomes conhecidos (comoModelViewMatrix
ou não) e depois armazená-los. Sua renderização pode mapear valores de enumeração comoMODEL_VIEW
passados para umaSetUniform
função de invólucro para examinar o sombreador associado, localizar o índice e chamarglUniform
adequadamente. 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 .
fonte
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
Material
classe abstrata . Os materiais agem como uma ponte entre o jogo e os shaders. CadaMaterial
um tem umaShader
e várias propriedades que podem ser definidas, incluindo texturas. Quando é hora de desenhar um objeto, eleMaterial
é vinculado. OBind
método possui umGraphicsState
parâmetro que contém todo o estado gráfico atual - matrizes, luzes etc. OBind
método vincula o shader e as texturas e define todos os uniformes, usando o que for necessárioGraphicsState
.fonte