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
#versionaufgrund 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.
glShaderSourceKann aufgrund dieser Aussage nicht verwendet werden, um Text vor den #versionDeklarationen voranzustellen . Dies bedeutet, dass die #versionZeile in Ihren glShaderSourceArgumenten 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 #versioninnerhalb des Skripts auf standardmäßige Weise angeben , müssen Sie #includenach der #versionAnweisung irgendwie -s einfügen . Dies könnte geschehen, indem der GLSL-Shader explizit analysiert wird, um die #versionZeichenfolge (falls vorhanden) zu finden und Ihre Einschlüsse danach zu machen, aber Zugriff auf eine#includeRichtlinie ist möglicherweise vorzuziehen, um leichter kontrollieren zu können, wann diese Einschlüsse vorgenommen werden müssen. Andererseits, da GLSL Kommentare vor der #versionZeile 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 #includeoder müssen Sie Ihre eigene Präprozessorerweiterung rollen?
Es gibt die GL_ARB_shading_language_includeErweiterung, 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 #includeMechanismus. Dies kann jedoch schwierig sein, da Sie auch andere Präprozessoranweisungen analysieren (und auswerten) #ifmü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, #includeda 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.