Есть куча подходов, но ни один не идеален.
Можно совместно использовать код, используя glAttachShader
для объединения шейдеров, но это не позволяет совместно использовать такие вещи, как объявления структуры или #define
константы -d. Это работает для обмена функциями.
Некоторым людям нравится использовать массив строк, переданных glShaderSource
как способ добавления общих определений перед вашим кодом, но у этого есть некоторые недостатки:
- Сложнее контролировать то, что должно быть включено из шейдера (для этого вам нужна отдельная система).
- Это означает, что автор шейдера не может указать GLSL
#version
из-за следующего утверждения в спецификации GLSL:
#Version директива должна произойти в шейдере , прежде чем что -то еще, для комментариев и белого пространства , за исключением.
Из-за этого заявления, glShaderSource
не может использоваться для добавления текста перед #version
объявлениями. Это означает, что #version
в ваши glShaderSource
аргументы должна быть включена строка , что означает, что интерфейсу компилятора GLSL нужно как-то сказать, какую версию GLSL предполагается использовать. Кроме того, отсутствие указания #version
сделает компилятор GLSL по умолчанию для использования GLSL версии 1.10. Если вы хотите, чтобы авторы шейдеров указывали #version
внутри скрипта стандартным способом, то вам нужно как-то вставить #include
-s после #version
оператора. Это можно сделать, явно проанализировав шейдер GLSL, чтобы найти #version
строку (если она есть) и сделать ваши включения после нее, но имея доступ к#include
Директива может быть предпочтительнее контролировать легче, когда эти включения должны быть сделаны. С другой стороны, поскольку GLSL игнорирует комментарии перед #version
строкой, вы можете добавить метаданные для включений в комментарии в верхней части вашего файла (черт.)
Теперь возникает вопрос: есть ли стандартное решение для #include
или вам нужно накатить собственное расширение препроцессора?
Существует GL_ARB_shading_language_include
расширение, но она имеет некоторые недостатки:
- Он поддерживается только NVIDIA ( http://delphigl.de/glcapsviewer/listreports2.php?listreportsbyextension=GL_ARB_shading_language_include )
- Это работает, определяя строки включения заранее. Поэтому перед компиляцией необходимо указать, что строка
"/buffers.glsl"
(как используется в #include "/buffers.glsl"
) соответствует содержимому файла buffer.glsl
(который вы загрузили ранее).
- Как вы, возможно, заметили в пункте (2), ваши пути должны начинаться с
"/"
абсолютных путей в стиле Linux. Эта нотация обычно незнакома программистам на Си и означает, что вы не можете указать относительные пути.
Обычный дизайн заключается в реализации собственного #include
механизма, но это может быть непросто, поскольку вам также необходимо проанализировать (и оценить) другие инструкции препроцессора, например #if
, для правильной обработки условной компиляции (например, защиты заголовка).
Если вы реализуете свой собственный #include
, у вас также есть некоторые свободы в том, как вы хотите его реализовать:
- Вы можете передать строки раньше времени (как
GL_ARB_shading_language_include
).
- Вы можете указать обратный вызов включения (это делается библиотекой DirectX D3DCompiler).
- Вы могли бы реализовать систему, которая всегда читает непосредственно из файловой системы, как это делается в типичных приложениях Си.
В качестве упрощения вы можете автоматически вставлять элементы защиты заголовков для каждого включения в свой уровень предварительной обработки, чтобы ваш процессорный уровень выглядел следующим образом:
if (#include and not_included_yet) include_file();
(Благодарим Трента Рида за показ вышеописанной техники.)
В заключение , не существует автоматического, стандартного и простого решения. В будущем решении вы могли бы использовать некоторый интерфейс SPIR-V OpenGL, и в этом случае компилятор GLSL-SPIR-V может находиться за пределами GL API. Наличие компилятора вне среды выполнения OpenGL значительно упрощает реализацию таких вещей, как, #include
поскольку это более подходящее место для взаимодействия с файловой системой. Я полагаю, что в настоящее время широко распространенный метод состоит в том, чтобы просто реализовать собственный препроцессор, который работает так, как должен быть знаком любой программист на Си.