OpenGL: Onde devo colocar shaders?


17

Estou tentando aprender o OpenGL ES 2.0 e estou me perguntando qual é a prática mais comum para "gerenciar" shaders.
Estou fazendo essa pergunta porque nos exemplos que encontrei (como o incluído na demonstração da API fornecida com o Android SDK), geralmente vejo tudo dentro da classe GLRenderer e prefiro separar as coisas para que eu possa ter, por exemplo, um objeto GLImage que eu possa reutilizar sempre que quiser desenhar um quad texturizado (estou focando 2D apenas no momento), como havia no código OpenGL ES 1.0. Em quase todos os exemplos que encontrei, os shaders são apenas definidos como atributos de classe. Por exemplo:

public class Square {

public final String vertexShader =
        "uniform mat4 uMVPMatrix;\n" +
        "attribute vec4 aPosition;\n" +
        "attribute vec4 aColor;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_Position = uMVPMatrix * aPosition;\n" +
        "  vColor = aColor;\n" +
        "}\n";

public final String fragmentShader =
        "precision mediump float;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_FragColor = vColor;\n" +
        "}\n";
// ...
}

Peço desculpas antecipadamente se algumas dessas perguntas são idiotas, mas nunca trabalhei com shaders antes.

1) O código acima é a maneira comum de definir shaders (propriedades públicas de classe final)?
2) Devo ter uma classe Shader separada?
3) Se shaders forem definidos fora da classe que os utiliza, como eu saberia os nomes de seus atributos (por exemplo, "aColor" no seguinte trecho de código) para poder vinculá-los?

colorHandle = GLES20.glGetAttribLocation(program, "aColor");

Respostas:


16

Eu sempre não gostei dessa maneira de definir shaders (em uma string). Eu prefiro fazer o meu em um arquivo de texto e lê-lo ao carregar. Defini-lo em uma string é irritante para a depuração e parece confuso para mim. É muito mais fácil digitar e vê-lo formatado como deveria, em vez de dentro de uma string.

Também tenho uma classe separada com funcionalidade de shader comum, como ler em shaders e imprimir informações de log para depuração de shader.

Onde quer que os shaders sejam definidos, você pode acessar os nomes exatamente como no seu exemplo. O uso da string literal é aceitável para shaders, pois é improvável que esses valores mudem depois que você os tiver implementado.

No entanto, no final, cabe a você. A maneira como você está fazendo isso funciona muito bem, se não estiver causando problemas.


2
Ter seus sombreadores em arquivos separados também significa que você pode usar seus editores de sombreador úteis e ter realce de sintaxe completo, e até visualizá-los antes de testá-los em seu programa, se isso for compatível.
Raceimaztion 11/12/12

6
A única razão real para ter sombreadores em uma string é para testes e tutoriais rápidos .. onde o manuseio de arquivos adiciona grande parte do "código desnecessário".
Jari Komppa

2
Você também pode implementar o hot-reload nos arquivos shader - permitindo depurar alterações sem reiniciar o jogo. Productivity ++
Liosan

Eu gosto da ideia de lê-los em um arquivo e ter uma classe Shader separada. Vê-los sempre em uma String estava me confundindo, porque eu não sabia se era assim que eles geralmente são definidos ou apenas para fins de aprendizado. Obrigado!
Miviclin

8

O gerenciamento de sombreador (e, portanto, material) é um problema bastante complicado que você encontra quando seu sistema gráfico se torna mais complexo e você nota que a codificação embutida em cada sombreador levaria a uma duplicação maciça de código. Aqui estão algumas maneiras alternativas de resolvê-lo:

  • Pequenos exemplos em que existem apenas alguns shaders tendem a codificá-los como strings para evitar o manuseio de arquivos, como comentou Jari Komppa
  • Melhor é usar arquivos separados, nos quais é possível destacar a sintaxe e formatar adequadamente. Você também pode codificar um sistema de depuração simples que observa as alterações nesses arquivos e aplica o shader modificado on-the-fly enquanto o jogo está em execução.
  • Quando a contagem de material aumenta, um gerador de shader se torna quase uma necessidade. Basicamente, você define snippets comuns como "snippet de mapeamento normal de shader de fragmento" e compõe seus shaders completos incluindo os componentes desejados.
    • Você pode usar trechos de strings codificados, mas isso fica muito confuso rapidamente, portanto, mais uma vez, os arquivos são uma boa idéia.
    • Você pode ter bits de código em arquivos separados (ou o mesmo) ou, alternativamente, usar o ubershader / supershader onde usa o pré-processador (ou sinalizadores uniformes) para ativar / desativar o material.

Quanto aos atributos e nomes uniformes, basta usar uma nomeação consistente em todos os shaders.


Definitivamente vou mover meus shaders para um arquivo separado. Obrigado!
Miviclin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.