Є купа підходів, але жоден не ідеальний.
Ділитися кодом можна за допомогою 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
рядком, ви можете додати метадані для включення до коментарів у верхній частині вашого файлу (yuck.)
Питання зараз: чи є стандартне рішення для #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. Це позначення, як правило, незнайоме програмістам на C, і означає, що ви не можете вказати відносні шляхи.
Звичайна конструкція полягає в реалізації власного #include
механізму, але це може бути складним, оскільки вам також потрібно проаналізувати (і оцінити) інші інструкції препроцесора, як-от #if
для правильної обробки умовної компіляції (наприклад, заголовки заголовків.)
Якщо ви реалізуєте свою власну #include
, у вас також є деякі свободи у тому, як ви хочете її реалізувати:
- Ви можете передавати рядки раніше часу (як
GL_ARB_shading_language_include
).
- Ви можете вказати зворотний виклик включення (це робиться бібліотекою Direct3 D3DCompiler.)
- Ви можете реалізувати систему, яка завжди читає безпосередньо з файлової системи, як це робиться у типових програмах C.
Для спрощення ви можете автоматично вставити захисні заголовки для кожного з них, що включаються у ваш попередній обробний шар, щоб ваш процесорний рівень виглядав так:
if (#include and not_included_yet) include_file();
(Подяка Тренту Риду за те, що він показав мені вищевказану техніку.)
На закінчення , не існує автоматичного, стандартного та простого рішення. У майбутньому рішенні ви можете використовувати деякий інтерфейс OpenGL SPIR-V, і в цьому випадку компілятор GLSL до SPIR-V може бути поза межами API API. Наявність компілятора за межами виконання OpenGL значно спрощує реалізацію таких речей, як #include
це більш підходяще місце для взаємодії з файловою системою. Я вважаю, що сучасний розповсюджений метод - це просто реалізувати спеціальний препроцесор, який працює таким чином, з яким повинен бути знайомий будь-який програміст C.