Istnieje wiele podejść, ale żadne nie jest idealne.
Możliwe jest współdzielenie kodu za pomocą glAttachShader
łączenia shaderów, ale nie pozwala to na współdzielenie takich rzeczy jak deklaracje struktur lub #define
stałe -d. Działa w przypadku funkcji udostępniania.
Niektórzy ludzie lubią wykorzystywać tablicę ciągów przekazywanych glShaderSource
jako sposób na przygotowanie wspólnych definicji przed kodem, ale ma to pewne wady:
- Trudniej jest kontrolować, co musi być zawarte w module cieniującym (potrzebujesz do tego osobnego systemu).
- Oznacza to, że autor modułu cieniującego nie może określić GLSL z
#version
powodu następującej instrukcji w specyfikacji GLSL:
#Version dyrektywa musi nastąpić w shader zanim cokolwiek innego, z wyjątkiem uwag i białej przestrzeni.
Ze względu na to oświadczenie glShaderSource
nie można używać do dodawania tekstu przed #version
deklaracjami. Oznacza to, że #version
wiersz musi zostać uwzględniony w glShaderSource
argumentach, co oznacza, że interfejs kompilatora GLSL musi w jakiś sposób zostać poinformowany, jakiej wersji GLSL należy użyć. Dodatkowo, brak określenia a #version
spowoduje, że kompilator GLSL będzie domyślnie używał GLSL w wersji 1.10. Jeśli chcesz pozwolić autorom modułu cieniującego na określenie #version
skryptu w standardowy sposób, musisz w jakiś sposób wstawić #include
-s po #version
instrukcji. Można to zrobić poprzez jawne parsowanie modułu cieniującego GLSL w celu znalezienia #version
ciągu (jeśli jest obecny) i wykonania po nim inkluzji, ale mając dostęp do#include
Zaleca się łatwiejsze kontrolowanie, gdy konieczne jest dokonanie tych inkluzji. Z drugiej strony, ponieważ GLSL ignoruje komentarze przed #version
wierszem, możesz dodać metadane dla uwzględnienia w komentarzach u góry pliku (fuj.)
Pytanie brzmi teraz: czy istnieje standardowe rozwiązanie #include
, czy też trzeba stworzyć własne rozszerzenie preprocesora?
Jest GL_ARB_shading_language_include
rozszerzenie, ale ma pewne wady:
- Jest obsługiwany tylko przez NVIDIA ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include )
- Działa poprzez określenie z wyprzedzeniem łańcuchów dołączania. Dlatego przed kompilacją musisz określić, że łańcuch
"/buffers.glsl"
(tak jak jest używany w #include "/buffers.glsl"
) odpowiada zawartości pliku buffer.glsl
(który wcześniej załadowałeś).
- Jak zapewne zauważyłeś w punkcie (2), twoje ścieżki muszą zaczynać
"/"
, podobnie jak ścieżki absolutne w stylu Linux. Ta notacja jest na ogół nieznana programistom języka C i oznacza, że nie można określić ścieżek względnych.
Typowym projektem jest implementacja własnego #include
mechanizmu, ale może to być trudne, ponieważ trzeba również przeanalizować (i ocenić) inne instrukcje preprocesora, takie jak #if
w celu prawidłowej obsługi kompilacji warunkowej (np. Osłony nagłówków).
Jeśli wdrożysz własne #include
, masz także pewne swobody w tym, jak chcesz je wdrożyć:
- Możesz przekazać ciągi z wyprzedzeniem (jak
GL_ARB_shading_language_include
).
- Można określić funkcję zwrotną dołączania (czynność tę wykonuje biblioteka D3DCompiler biblioteki DirectX).
- Można zaimplementować system, który zawsze odczytuje bezpośrednio z systemu plików, jak ma to miejsce w typowych aplikacjach C.
Upraszczając, możesz automatycznie wstawiać osłony nagłówków dla każdego z nich w warstwie przetwarzania wstępnego, dzięki czemu warstwa procesora wygląda następująco:
if (#include and not_included_yet) include_file();
(Podziękowania dla Trenta Reeda za pokazanie mi powyższej techniki.)
Podsumowując , nie ma automatycznego, standardowego i prostego rozwiązania. W przyszłym rozwiązaniu można użyć interfejsu SPIR-V OpenGL, w którym to przypadku kompilator GLSL na SPIR-V może znajdować się poza interfejsem GL API. Posiadanie kompilatora poza środowiskiem wykonawczym OpenGL znacznie upraszcza implementację takich rzeczy, #include
ponieważ jest to bardziej odpowiednie miejsce do komunikacji z systemem plików. Uważam, że obecną szeroko rozpowszechnioną metodą jest po prostu wdrożenie niestandardowego preprocesora, który działa w sposób, który powinien znać każdy programista C.