Há várias abordagens, mas nenhuma é perfeita.
É possível compartilhar código usando glAttachShader
para combinar shaders, mas isso não torna possível compartilhar coisas como declarações struct ou #define
constantes -d. Ele funciona para compartilhar funções.
Algumas pessoas gostam de usar a matriz de seqüências de caracteres transmitidas glShaderSource
como uma maneira de anexar definições comuns antes do seu código, mas isso tem algumas desvantagens:
- É mais difícil controlar o que precisa ser incluído no shader (você precisa de um sistema separado para isso).
- Isso significa que o autor do sombreador não pode especificar o GLSL
#version
, devido à seguinte declaração na especificação do GLSL:
A diretiva #version deve ocorrer em um sombreador antes de qualquer outra coisa, exceto comentários e espaço em branco.
Devido a esta declaração, glShaderSource
não pode ser usado para acrescentar texto antes das #version
declarações. Isso significa que a #version
linha precisa ser incluída em seus glShaderSource
argumentos, o que significa que sua interface do compilador GLSL precisa, de alguma forma, ser informada sobre qual versão do GLSL deve ser usada. Além disso, a não especificação de a #version
fará o compilador GLSL usar como padrão o GLSL versão 1.10. Se você deseja permitir que os autores do shader especifiquem o #version
script de uma maneira padrão, será necessário inserir #include
-s após a #version
instrução. Isso pode ser feito analisando explicitamente o sombreador GLSL para encontrar a #version
string (se presente) e faça suas inclusões depois dela, mas tendo acesso a um#include
diretiva pode ser preferível controlar mais facilmente quando essas inclusões precisam ser feitas. Por outro lado, como o GLSL ignora comentários antes da #version
linha, você pode adicionar metadados para inclusões nos comentários na parte superior do seu arquivo (eca.)
A questão agora é: Existe uma solução padrão para #include
, ou você precisa lançar sua própria extensão de pré-processador?
Existe a GL_ARB_shading_language_include
extensão, mas tem algumas desvantagens:
- É suportado apenas pela NVIDIA ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include )
- Ele funciona especificando as seqüências de inclusão antecipadamente. Portanto, antes de compilar, você precisa especificar que a sequência
"/buffers.glsl"
(conforme usada em #include "/buffers.glsl"
) corresponde ao conteúdo do arquivo buffer.glsl
(que você carregou anteriormente).
- Como você deve ter notado no ponto (2), seus caminhos precisam começar
"/"
, como caminhos absolutos no estilo Linux. Essa notação geralmente não é familiar para programadores C e significa que você não pode especificar caminhos relativos.
Um design comum é implementar seu próprio #include
mecanismo, mas isso pode ser complicado, pois você também precisa analisar (e avaliar) outras instruções do pré-processador, como #if
para lidar adequadamente com a compilação condicional (como proteções de cabeçalho).
Se você implementar o seu próprio #include
, também terá algumas liberdades em como deseja implementá-lo:
- Você pode passar as strings antes do tempo (como
GL_ARB_shading_language_include
).
- Você pode especificar um retorno de chamada de inclusão (isso é feito pela biblioteca D3DCompiler do DirectX.)
- Você pode implementar um sistema que sempre lê diretamente do sistema de arquivos, como é feito em aplicativos C.
Como simplificação, você pode inserir automaticamente protetores de cabeçalho para cada inclusão na camada de pré-processamento, para que a camada do processador se pareça com:
if (#include and not_included_yet) include_file();
(Agradecemos a Trent Reed por me mostrar a técnica acima.)
Em conclusão , não existe solução automática, padrão e simples. Em uma solução futura, você pode usar alguma interface SPIR-V OpenGL; nesse caso, o compilador GLSL para SPIR-V pode estar fora da API GL. Ter o compilador fora do tempo de execução do OpenGL simplifica muito a implementação de coisas como #include
é um local mais apropriado para a interface com o sistema de arquivos. Acredito que o atual método difundido é apenas implementar um pré-processador personalizado que funcione da maneira que qualquer programador C deve estar familiarizado.