Il y a un tas d'approches, mais aucune n'est parfaite.
Il est possible de partager du code en utilisant glAttachShader
pour combiner des shaders, mais cela ne permet pas de partager des choses comme des déclarations de structure ou des #define
constantes -d. Cela fonctionne pour le partage des fonctions.
Certaines personnes aiment utiliser le tableau de chaînes passé pour glShaderSource
ajouter des définitions communes avant votre code, mais cela présente certains inconvénients:
- Il est plus difficile de contrôler ce qui doit être inclus depuis le shader (vous avez besoin d'un système séparé pour cela.)
- Cela signifie que l'auteur du shader ne peut pas spécifier le GLSL
#version
, en raison de la déclaration suivante dans la spécification GLSL:
La directive #version doit apparaître avant tout dans un shader, sauf pour les commentaires et les espaces blancs.
En raison de cette déclaration, glShaderSource
ne peut pas être utilisé pour ajouter du texte avant les #version
déclarations. Cela signifie que la #version
ligne doit être incluse dans vos glShaderSource
arguments, ce qui signifie que votre interface de compilation GLSL doit en quelque sorte être informée de la version de GLSL qui devrait être utilisée. De plus, le fait de ne pas spécifier de #version
rendra le compilateur GLSL par défaut pour utiliser GLSL version 1.10. Si vous souhaitez laisser les auteurs de shader spécifier le #version
contenu du script de manière standard, vous devez en quelque sorte insérer #include
-s après l' #version
instruction. Cela pourrait être fait en analysant explicitement le shader GLSL pour trouver la #version
chaîne (si elle est présente) et faire vos inclusions après, mais en ayant accès à un#include
La directive pourrait être préférable de contrôler plus facilement quand ces inclusions doivent être faites. D'autre part, puisque GLSL ignore les commentaires avant la #version
ligne, vous pouvez ajouter des métadonnées pour les inclus dans les commentaires en haut de votre fichier (beurk).
La question est maintenant: existe-t-il une solution standard pour #include
, ou avez-vous besoin de rouler votre propre extension de préprocesseur?
Il y a l' GL_ARB_shading_language_include
extension, mais elle a quelques inconvénients:
- Il est uniquement pris en charge par NVIDIA ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include )
- Il fonctionne en spécifiant les chaînes d'inclusion à l'avance. Par conséquent, avant de compiler, vous devez spécifier que la chaîne
"/buffers.glsl"
(utilisée dans #include "/buffers.glsl"
) correspond au contenu du fichier buffer.glsl
(que vous avez chargé précédemment).
- Comme vous l'avez peut-être remarqué au point (2), vos chemins doivent commencer par
"/"
, comme les chemins absolus de style Linux. Cette notation n'est généralement pas familière aux programmeurs C et signifie que vous ne pouvez pas spécifier de chemins relatifs.
Une conception courante consiste à implémenter votre propre #include
mécanisme, mais cela peut être délicat car vous devez également analyser (et évaluer) d'autres instructions de préprocesseur comme #if
pour gérer correctement la compilation conditionnelle (comme les gardes d'en-tête.)
Si vous implémentez le vôtre #include
, vous avez également quelques libertés dans la façon dont vous souhaitez l'implémenter:
- Vous pouvez passer des chaînes à l'avance (comme
GL_ARB_shading_language_include
).
- Vous pouvez spécifier un rappel d'inclusion (cela se fait par la bibliothèque D3DCompiler de DirectX.)
- Vous pouvez implémenter un système qui lit toujours directement à partir du système de fichiers, comme dans les applications C typiques.
Pour simplifier, vous pouvez insérer automatiquement des protections d'en-tête pour chaque inclusion dans votre couche de prétraitement, afin que votre couche de processeur ressemble à:
if (#include and not_included_yet) include_file();
(Nous remercions Trent Reed de m'avoir montré la technique ci-dessus.)
En conclusion , il n'existe pas de solution automatique, standard et simple. Dans une future solution, vous pourriez utiliser une interface OpenGL SPIR-V, auquel cas le compilateur GLSL vers SPIR-V pourrait être en dehors de l'API GL. Le fait d'avoir le compilateur en dehors du runtime OpenGL simplifie considérablement la mise en œuvre de choses comme, #include
car c'est un endroit plus approprié pour s'interfacer avec le système de fichiers. Je crois que la méthode répandue actuelle consiste simplement à implémenter un préprocesseur personnalisé qui fonctionne d'une manière que tout programmeur C devrait être familier.