Hay muchos enfoques, pero ninguno es perfecto.
Es posible compartir código mediante la glAttachShader
combinación de sombreadores, pero esto no permite compartir cosas como declaraciones de estructura o #define
constantes -d. Funciona para compartir funciones.
A algunas personas les gusta usar el conjunto de cadenas que se pasan glShaderSource
como una forma de anteponer definiciones comunes antes de su código, pero esto tiene algunas desventajas:
- Es más difícil controlar lo que debe incluirse desde el sombreador (necesita un sistema separado para esto).
- Significa que el autor del sombreador no puede especificar el GLSL
#version
, debido a la siguiente declaración en la especificación GLSL:
La directiva #version debe aparecer en un sombreador antes que cualquier otra cosa, excepto los comentarios y los espacios en blanco.
Debido a esta declaración, glShaderSource
no se puede usar para anteponer texto antes de las #version
declaraciones. Esto significa que la #version
línea debe incluirse en sus glShaderSource
argumentos, lo que significa que su interfaz de compilador GLSL necesita saber de alguna manera qué versión de GLSL se espera que se use. Además, al no especificar a #version
, el compilador GLSL usará GLSL versión 1.10 de forma predeterminada. Si desea permitir que los autores de sombreadores especifiquen #version
dentro del script de una manera estándar, entonces debe insertar #include
-s de alguna manera después de la #version
declaración. Esto podría hacerse analizando explícitamente el sombreador GLSL para encontrar la #version
cadena (si está presente) y hacer sus inclusiones después de ella, pero teniendo acceso a un#include
La directiva podría ser preferible para controlar más fácilmente cuando esas inclusiones deben hacerse. Por otro lado, dado que GLSL ignora los comentarios antes de la #version
línea, puede agregar metadatos para incluirlos dentro de los comentarios en la parte superior de su archivo (qué asco).
La pregunta ahora es: ¿hay una solución estándar para #include
, o necesita rodar su propia extensión de preprocesador?
Existe la GL_ARB_shading_language_include
extensión, pero tiene algunos inconvenientes:
- Solo es compatible con NVIDIA ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include )
- Funciona especificando las cadenas de inclusión con anticipación. Por lo tanto, antes de compilar, debe especificar que la cadena
"/buffers.glsl"
(como se usa en #include "/buffers.glsl"
) corresponde al contenido del archivo buffer.glsl
(que ha cargado previamente).
- Como habrás notado en el punto (2), tus rutas deben comenzar
"/"
, como las rutas absolutas de estilo Linux. Esta notación generalmente no es familiar para los programadores de C, y significa que no puede especificar rutas relativas.
Un diseño común es implementar su propio #include
mecanismo, pero esto puede ser complicado ya que también necesita analizar (y evaluar) otras instrucciones del preprocesador #if
para manejar adecuadamente la compilación condicional (como los protectores de encabezado).
Si implementa el suyo propio #include
, también tiene algunas libertades en cómo desea implementarlo:
- Podrías pasar cadenas por adelantado (como
GL_ARB_shading_language_include
).
- Puede especificar una devolución de llamada de inclusión (esto se hace mediante la biblioteca D3DCompiler de DirectX).
- Puede implementar un sistema que siempre lea directamente desde el sistema de archivos, como se hace en las aplicaciones C típicas.
Como simplificación, puede insertar automáticamente protectores de encabezado para cada inclusión en su capa de preprocesamiento, para que su capa de procesador se vea así:
if (#include and not_included_yet) include_file();
(Gracias a Trent Reed por mostrarme la técnica anterior).
En conclusión , no existe una solución automática, estándar y simple. En una solución futura, podría usar alguna interfaz SPIR-V OpenGL, en cuyo caso el compilador GLSL a SPIR-V podría estar fuera de la API GL. Tener el compilador fuera del tiempo de ejecución de OpenGL simplifica enormemente la implementación de cosas como, #include
ya que es un lugar más apropiado para interactuar con el sistema de archivos. Creo que el método generalizado actual es simplemente implementar un preprocesador personalizado que funcione de una manera con la que cualquier programador de C debería estar familiarizado.