sunLoadingImage
whowedImag
decoration left 1
decoration left 2
transhome
transprojects
transgallery
transarticles
decoration rigth
English
Українська
Show/Hide search bar
black cat logo variable logo
[20 Січ 2013]

Шейдерні підпрограми в GLSL

Іноді виникає необхідність зміни поведінки шейдера. Цього можна досягти зміною шейдера на інший шейдер, але операція встановлення нового шейдера може бути тривалою та понизити швидкість виконання програми. Можна змінювати поведінку шейдера через встановлення uniform значення, і в залежності від нього обирати блок коду, який має виконуватися (наприклад, перевірками через if). Але це також знизить швидкість виконання. Цю проблему можна вирішити з допомогою шейдерних підпрограм. Шейдерні підпрограми використовують принцип схожий до вказівників на функції в C++: можна робити виклик функції на яку вказує вказівник. Спочатку потрібно оголосити тип підпрограми - формат вхідних та вихідних параметрів, а також назву типу. Потім потрібно оголосити функції, інтерфейс яких співпадає з типом підпрограми. І останній крок - оголосити uniform змінну підпрограми, яка виступає в ролі вказівника на функцію. Перед рендерінгом необхідно через цю uniform змінну задати, яка функція з сумісних має викликатися підпрограмою. По замовчуванню uniform підпрограма виконує першу з сумісних функцій.
// оголосити тип підпрограми
// subroutine returnType subroutineType(type param1, ...);
subroutine vec4 testType();

// оголосити функції з заданим типом, які можна буде обирати динамічно
// subroutine (subroutineType) returnType functionName(type param1, ...);
subroutine (testType) vec4 func1()
{
   return vec4(1, 1, 1, 0);
}
subroutine (testType) vec4 func2()
{
   return vec4(0.5, 0.5, 0.5, 0.5);
}

// uniform змінна, яка буде виступати в ролі вказівника на функцію
subroutine uniform testType dynamicFunction;

// викликати uniform змінну в необхідному місці
dynamicFunction();
Перед рендерінгом необхідно встановити функцію, яка буде використовуватися підпрограмою. Спочатку потрібно встановити шейдер активним з допомогою glUseProgram(). Дістаємо локацію uniform підпрограми, щоб встановити фукнцію, яка буде виконуватися нею. Спеціальна функція, щоб дістати розмішення такої uniform змінної - glGetSubroutineUniformLocation().
Після цього з шейдера потрібно дізнатися індекси функцій, які є сумісними з типом підпрограми. Для цього використовується glGetSubroutineIndex().
Задати, яка функція має виконуватися підпрограмою у шейдері можна з допомогою функції glUniformSubroutinesuiv(). Функції передається масив індексів. Кожен індекс відповідає uniform підпрограмі. Значення індексу відповідає індексу функції, яку буде виконувати підпрограма. Кількість підпрорам, яким необхідно встановити індекси функцій можна дізнатися з допомогою запиту значення GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS.
Далі наведено приклад того як вибрати функцію func1 для виконання підпрограмою:
// вибрати шейдерну програму
glUseProgram(shaderProgram);

// дістати порядковий індекс uniform підпрограми
GLint locationOfFunction = glGetSubroutineUniformLocation(
         shaderProgram, GL_FRAGMENT_SHADER, "dynamicFunction");

// дістати порядкові індекси можливих підпрограм
GLuint indexFunc1 = glGetSubroutineIndex(shaderProgram, GL_FRAGMENT_SHADER, "func1");
GLuint indexFunc2 = glGetSubroutineIndex(shaderProgram, GL_FRAGMENT_SHADER, "func2");

// дістати кількість активних підпрограм в поточному шейдері
GLsizei numActiveUniforms;
glGetProgramStageiv(shaderProgram, GL_FRAGMENT_SHADER,
         GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &numActiveUniforms);

// встановити індекси для усіх активних підпрограм
std::unique_ptr indices(new GLuint[numActiveUniforms]);
indices[locationOfFunction] = indexFunc1;
glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, numActiveUniforms, indices.get());
Дізнатися максимальну кількість підпрограм та доступних позицій для підпрограм
GLint maxSubroutines, maxSubroutinesUniformLocations;
glGetIntegerv(GL_MAX_SUBROUTINES, &maxSubroutines);
glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &maxSubroutinesUniformLocations);
Також можна дізнатися усі наявні в шейдері підпрограми та сумісні з функції:
GLint numActiveSubroutinveUniforms, numCompatibleSubroutines, tLen;
const int maxNameSize = 64;
char tName[maxNameSize];

// необхідно встановити поточний шейдер для якого будуть шукатися параметри
glUseProgram(shaderProgram);

// дістати кількість uniform підпрограм з шейдера
glGetProgramStageiv(shaderProgram, GL_FRAGMENT_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &numActiveSubroutinveUniforms);

// зібрати дані для кожної uniform підпрограми
for (int uniformSubroutine = 0; uniformSubroutine < numActiveSubroutinveUniforms; ++uniformSubroutine)
{
   // дістати назву uniform підпрограми з індексом uniformSubroutine
   glGetActiveSubroutineUniformName(
         shaderProgram,
         GL_FRAGMENT_SHADER,
         uniformSubroutine,
         maxNameSize,
         &tLen,
         tName
      );

   // дістати кількість функцій сумісних з підпрограмою
   GLint numCompatibleSubroutines;
   glGetActiveSubroutineUniformiv(
         shaderProgram,
         GL_FRAGMENT_SHADER,
         uniformSubroutine,
         GL_NUM_COMPATIBLE_SUBROUTINES,
         &numCompatibleSubroutines
      );

   // дістати індекси функцій сумісних з підпрограмою
   std::unique_ptr indices(new GLint[numCompatibleSubroutines]);
   glGetActiveSubroutineUniformiv(
         shaderProgram,
         GL_FRAGMENT_SHADER,
         uniformSubroutine,
         GL_COMPATIBLE_SUBROUTINES,
         indices.get()
      );

   // дістати назви функцій сумісних з підпрограмою
   for(int compatibleSubroutine=0;
      compatibleSubroutine<numCompatibleSubroutines; compatibleSubroutine++)
   {
      glGetActiveSubroutineName(
            shaderProgram,
            GL_FRAGMENT_SHADER,
            indices[compatibleSubroutine],
            maxNameSize,
            &tLen,
            tName
         );
   }
}



Sun and Black Cat- Ігор Дихта (igor dykhta email) © 2007-2014