Es gibt eine Reihe von Ansätzen, aber keiner ist perfekt.
Es ist möglich, Code gemeinsam zu nutzen glAttachShader
, indem Shader kombiniert werden. Dies macht es jedoch nicht möglich, Dinge wie Strukturdeklarationen oder #define
-d-Konstanten gemeinsam zu nutzen. Es funktioniert für das Teilen von Funktionen.
Einige Leute möchten das Array von Zeichenfolgen, an das übergeben wird, verwenden glShaderSource
, um allgemeine Definitionen vor Ihren Code zu stellen. Dies hat jedoch einige Nachteile:
- Es ist schwieriger zu steuern, was im Shader enthalten sein muss (Sie benötigen hierfür ein separates System.)
- Dies bedeutet, dass der Shader-Autor die GLSL
#version
aufgrund der folgenden Anweisung in der GLSL-Spezifikation nicht angeben kann :
Die Anweisung #version muss in einem Shader vor allen anderen Anweisungen außer Kommentaren und Leerzeichen stehen.
glShaderSource
Kann aufgrund dieser Aussage nicht verwendet werden, um Text vor den #version
Deklarationen voranzustellen . Dies bedeutet, dass die #version
Zeile in Ihren glShaderSource
Argumenten enthalten sein muss, was bedeutet, dass Ihrer GLSL-Compiler-Schnittstelle irgendwie mitgeteilt werden muss, welche Version von GLSL verwendet werden soll. Wenn Sie kein angeben #version
, verwendet der GLSL-Compiler standardmäßig die GLSL-Version 1.10. Wenn Sie möchten, dass Shader-Autoren das #version
innerhalb des Skripts auf standardmäßige Weise angeben , müssen Sie #include
nach der #version
Anweisung irgendwie -s einfügen . Dies könnte geschehen, indem der GLSL-Shader explizit analysiert wird, um die #version
Zeichenfolge (falls vorhanden) zu finden und Ihre Einschlüsse danach zu machen, aber Zugriff auf eine#include
Richtlinie ist möglicherweise vorzuziehen, um leichter kontrollieren zu können, wann diese Einschlüsse vorgenommen werden müssen. Andererseits, da GLSL Kommentare vor der #version
Zeile ignoriert , können Sie Metadaten für Includes in Kommentaren am Anfang Ihrer Datei hinzufügen (yuck.)
Die Frage ist nun: Gibt es eine Standardlösung für #include
oder müssen Sie Ihre eigene Präprozessorerweiterung rollen?
Es gibt die GL_ARB_shading_language_include
Erweiterung, aber sie hat einige Nachteile:
- Es wird nur von NVIDIA unterstützt ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include ).
- Es funktioniert, indem die Einschlusszeichenfolgen vorab angegeben werden. Daher müssen Sie vor dem Kompilieren angeben, dass die Zeichenfolge
"/buffers.glsl"
(wie in verwendet #include "/buffers.glsl"
) dem Inhalt der Datei buffer.glsl
(die Sie zuvor geladen haben) entspricht.
- Wie Sie vielleicht in Punkt (2) bemerkt haben, müssen Ihre Pfade mit
"/"
absoluten Pfaden im Linux-Stil beginnen. Diese Notation ist C-Programmierern im Allgemeinen unbekannt und bedeutet, dass Sie keine relativen Pfade angeben können.
Ein gängiger Entwurf ist die Implementierung eines eigenen #include
Mechanismus. Dies kann jedoch schwierig sein, da Sie auch andere Präprozessoranweisungen analysieren (und auswerten) #if
müssen, um die bedingte Kompilierung ordnungsgemäß zu handhaben (z. B. Header-Guards).
Wenn Sie Ihre eigenen implementieren #include
, haben Sie auch einige Freiheiten bei der Implementierung:
- Sie könnten Zeichenfolgen vorzeitig übergeben (wie
GL_ARB_shading_language_include
).
- Sie können einen Include-Rückruf angeben (dies wird von der D3DCompiler-Bibliothek von DirectX ausgeführt.)
- Sie können ein System implementieren, das immer direkt aus dem Dateisystem liest, wie in typischen C-Anwendungen.
Zur Vereinfachung können Sie automatisch Header-Guards für jedes Include in Ihre Vorverarbeitungsebene einfügen, sodass Ihre Prozessorebene wie folgt aussieht:
if (#include and not_included_yet) include_file();
(Dank an Trent Reed, der mir die oben beschriebene Technik gezeigt hat.)
Zusammenfassend gibt es keine automatische, standardmäßige und einfache Lösung. In einer zukünftigen Lösung könnten Sie eine SPIR-V OpenGL-Schnittstelle verwenden. In diesem Fall könnte sich der GLSL-zu-SPIR-V-Compiler außerhalb der GL-API befinden. Wenn der Compiler außerhalb der OpenGL-Laufzeitumgebung installiert ist, vereinfacht sich die Implementierung erheblich, #include
da die Schnittstelle zum Dateisystem besser geeignet ist. Ich glaube, die derzeit weit verbreitete Methode besteht darin, nur einen benutzerdefinierten Präprozessor zu implementieren, der so funktioniert, wie es sich jeder C-Programmierer vorstellen sollte.