Є купа підходів, але жоден не ідеальний.
Ділитися кодом можна за допомогою 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.