C'è un sacco di approcci, ma nessuno è perfetto.
È possibile condividere il codice usando glAttachShader
per combinare shader, ma ciò non rende possibile condividere cose come dichiarazioni di struttura o #define
costanti -d. Funziona per condividere le funzioni.
Ad alcune persone piace usare la matrice di stringhe passate glShaderSource
come modo per anteporre definizioni comuni prima del codice, ma questo presenta alcuni svantaggi:
- È più difficile controllare ciò che deve essere incluso all'interno dello shader (per questo è necessario un sistema separato).
- Significa che l'autore dello shader non può specificare il GLSL
#version
, a causa della seguente dichiarazione nelle specifiche GLSL:
La direttiva #version deve essere presente in uno shader prima di ogni altra cosa, ad eccezione dei commenti e degli spazi bianchi.
A causa di questa affermazione, glShaderSource
non può essere utilizzato per anteporre il testo prima delle #version
dichiarazioni. Ciò significa che la #version
linea deve essere inclusa nei tuoi glShaderSource
argomenti, il che significa che l'interfaccia del compilatore GLSL deve essere in qualche modo informata su quale versione di GLSL dovrebbe essere utilizzata. Inoltre, non specificando a #version
si renderà il compilatore GLSL predefinito usando GLSL versione 1.10. Se vuoi lasciare che gli autori dello shader specifichino l' #version
interno dello script in modo standard, allora devi in qualche modo inserire #include
-s dopo l' #version
istruzione. Questo potrebbe essere fatto analizzando esplicitamente lo shader GLSL per trovare la #version
stringa (se presente) e fare le tue inclusioni dopo di essa, ma avere accesso a un#include
la direttiva potrebbe essere preferibile per controllare più facilmente quando tali inclusioni devono essere fatte. D'altra parte, poiché GLSL ignora i commenti prima della #version
riga, è possibile aggiungere metadati per le inclusioni all'interno dei commenti nella parte superiore del file (yuck.)
La domanda ora è: esiste una soluzione standard per #include
o è necessario implementare la propria estensione preprocessore?
C'è l' GL_ARB_shading_language_include
estensione, ma ha alcuni svantaggi:
- È supportato solo da NVIDIA ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include )
- Funziona specificando in anticipo le stringhe di inclusione. Pertanto, prima di compilare, è necessario specificare che la stringa
"/buffers.glsl"
(come utilizzata in #include "/buffers.glsl"
) corrisponde al contenuto del file buffer.glsl
(che è stato caricato in precedenza).
- Come avrai notato al punto (2), i tuoi percorsi devono iniziare con
"/"
, come i percorsi assoluti in stile Linux. Questa notazione è generalmente sconosciuta ai programmatori C e significa che non è possibile specificare percorsi relativi.
Un design comune è quello di implementare il tuo #include
meccanismo, ma questo può essere complicato poiché è necessario analizzare (e valutare) altre istruzioni del preprocessore come #if
per gestire correttamente la compilazione condizionale (come le protezioni delle intestazioni).
Se implementi il tuo #include
, hai anche alcune libertà su come vuoi implementarlo:
- Potresti passare le stringhe in anticipo (come
GL_ARB_shading_language_include
).
- È possibile specificare un callback include (questo viene fatto dalla libreria del compilatore D3DC di DirectX).
- Potresti implementare un sistema che legge sempre direttamente dal filesystem, come fatto nelle tipiche applicazioni C.
Come semplificazione, è possibile inserire automaticamente protezioni di intestazione per ciascuna inclusione nel livello di preelaborazione, in modo che il livello del processore sia simile a:
if (#include and not_included_yet) include_file();
(Ringraziamo Trent Reed per avermi mostrato la tecnica sopra.)
In conclusione , non esiste una soluzione automatica, standard e semplice. In una soluzione futura, è possibile utilizzare un'interfaccia OpenGL SPIR-V, nel qual caso il compilatore da GLSL a SPIR-V potrebbe trovarsi al di fuori dell'API GL. Avere il compilatore fuori dal runtime OpenGL semplifica notevolmente l'implementazione di cose come #include
poiché è un posto più appropriato per interfacciarsi con il filesystem. Credo che l'attuale metodo diffuso sia semplicemente implementare un preprocessore personalizzato che funzioni in modo che qualsiasi programmatore C dovrebbe avere familiarità.