Отображение атрибутов вершинных шейдеров в GLSL
Я пишу небольшой движок рендеринга с шейдерами GLSL:
Каждая сетка (ну, подмешивание) имеет несколько потоков вершин (например, положение, нормаль, текстура, тангенс и т. Д.) В один большой VBO и MaterialID.
Каждый Материал имеет набор текстур и свойств (например, зеркальный цвет, диффузный цвет, цветная текстура, карта нормалей и т. Д.)
Тогда у меня есть шейдер GLSL, с его униформой и атрибутами. Скажем так:
uniform vec3 DiffuseColor;
uniform sampler2D NormalMapTexture;
attribute vec3 Position;
attribute vec2 TexCoord;
Я немного застрял в попытке разработать способ для шейдера GLSL определять потоковые отображения (семантику) для атрибутов и униформ, а затем связывать потоки вершин с соответствующими атрибутами.
Что-то в строках с надписью к сетке:"поместите ваш поток позиции в атрибут" Положение ", а ваши текс-координаты в"TexCoord". Также поместите диффузный цвет вашего материала в"DiffuseColor"и вторую текстуру вашего материала в"NormalMapTexture"
В настоящее время я использую жестко закодированные имена для атрибутов (т. Е. Vertex pos - это всегда "Position" и т. Д.) И проверяю каждую униформу и имя атрибута, чтобы понять, для чего это использует шейдер.
Я думаю, что я ищу какой-то способ создания "объявления вершины", но также включая униформу и текстуры.
Поэтому мне просто интересно, как люди делают это в масштабных движках рендеринга.
Редактировать:
Резюме предлагаемых методов:
1. Атрибут / Унифицированная семантика задается именем переменной(что я сейчас делаю). Использование предварительно определенных имен для каждого возможного атрибута. Связыватель GLSL запросит имя для каждого атрибута и свяжет массив вершин на основе имя переменной:
//global static variable
semantics (name,normalize,offset) = {"Position",false,0} {"Normal",true,1},{"TextureUV,false,2}
...when linking
for (int index=0;index<allAttribs;index++)
{
glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);
semantics[index]= GetSemanticsFromGlobalHardCodedList(name);
}
... when binding vertex arrays for render
for (int index=0;index<allAttribs;index++)
{
glVertexAttribPointer(index,size[index],type[index],semantics[index]->normalized,bufferStride,semantics[index]->offset);
}
2. Предопределенные места для каждой семантики
Механизм связывания GLSL всегда будет связывать массивы вершин с одними и теми же местоположениями. Шейдер должен использовать соответствующие имена для сопоставления. (Это кажется очень похожим на метод 1, но если я не понял неправильно, это подразумевает связывание ВСЕХ доступных данных вершин, даже если шейдер их не использует)
.. when linking the program...
glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");
3. Словарь доступных атрибутов из Material, Engine globals, Renderer и Mesh
Вести список доступных атрибутов, опубликованных активным материалом, глобалами Engine, текущим рендерером и текущим узлом сцены.
например:
Material has (uniformName,value) = {"ambientColor", (1.0,1.0,1.0)}, {"diffuseColor",(0.2,0.2,0.2)}
Mesh has (attributeName,offset) = {"Position",0,},{"Normals",1},{"BumpBlendUV",2}
тогда в шейдер:
uniform vec3 ambientColor,diffuseColo;
attribute vec3 Position;
При связывании данных вершин с шейдером связыватель GLSL будет перебирать атрибуты и связываться с найденным (или нет?) В словаре:
for (int index=0;index<allAttribs;index++)
{
glGetActiveAttrib(program,index,bufSize,length,size[index],type[index],name);
semantics[index] = Mesh->GetAttributeSemantics(name);
}
и то же самое с униформой, запросите только активный материал и глобальные переменные.
3 ответа
Атрибуты:
Ваша сетка имеет несколько потоков данных. Для каждого потока вы можете сохранить следующую информацию: (имя, тип, данные).
После установления связи вы можете запросить в программе GLSL активные атрибуты и сформировать словарь атрибутов для этой программы. Каждый элемент здесь просто (имя, тип).
Когда вы рисуете сетку с указанной программой GLSL, вы просматриваете словарь атрибутов программ и связываете соответствующие потоки сетки (или сообщаете об ошибке в случае несоответствия).
Униформа:
Пусть словарь параметров шейдера будет набором (имя, тип, ссылка на данные). Как правило, вы можете иметь следующие словари:
- Материал (рассеянный, зеркальный, блестящий и т. Д.) - взят из материала
- Двигатель (камера, модель, фары, таймеры и т. Д.) - взят из двигателя синглтон (глобальный)
- Render (пользовательские параметры, связанные с создателем шейдера: радиус SSAO, степень размытия и т. Д.) - предоставляется исключительно классом создателя шейдера (render)
После компоновки программе GLSL предоставляется набор словарей параметров, чтобы заполнить свой собственный словарь следующим форматом элемента: (местоположение, тип, ссылка на данные). Эта популяция выполняется путем запроса списка активных униформ и сопоставления пары (имя, тип) с той, что в словарях.
Заключение: этот метод позволяет передавать любые пользовательские атрибуты вершин и униформу шейдеров без жестко закодированных имен / семантики в движке. В основном только загрузчик и рендер знают о конкретной семантике:
- Загрузчик заполняет декларации потоков данных сетки и словари материалов.
- В Render используется шейдер, который знает имена, предоставляет дополнительные параметры и выбирает нужные сетки для рисования.
Исходя из моего опыта, OpenGL не определяет концепцию атрибутов или семантики форм.
Все, что вы можете сделать, это определить свой собственный способ отображения вашей семантики в переменные OpenGL, используя единственный параметр, которым вы можете управлять для этих переменных: их местоположение.
Если вас не ограничивают проблемы с платформой, вы можете попробовать использовать "новый" GL_ARB_explicit_attrib_location (ядро в OpenGL 3.3, если я не ошибаюсь), который позволяет шейдерам явно выражать, какое местоположение предназначено для какого атрибута. Таким образом, вы можете жестко кодировать (или настраивать), какие данные вы хотите привязать к какому расположению атрибута, и запрашивать расположение шейдеров после его компиляции. Кажется, что эта функция еще не является зрелой и, возможно, подвержена ошибкам в различных драйверах.
Другой способ - привязать местоположения ваших атрибутов с помощью glBindAttribLocation. Для этого вам нужно знать имена атрибутов, которые вы хотите связать, и местоположения, которые вы хотите назначить им.
Чтобы узнать имена, используемые в шейдере, вы можете:
- запросить у шейдера активные атрибуты
- разобрать исходный код шейдера, чтобы найти их самостоятельно
Я бы не рекомендовал использовать способ синтаксического анализа GLSL (хотя он может удовлетворить ваши потребности, если вы находитесь в достаточно простых контекстах): препроцессор может легко победить анализатор. Предположим, что ваш шейдерный код становится несколько сложным, вы можете начать использовать #include, #defines, #ifdef и т. Д. Надежный синтаксический анализ предполагает наличие надежного препроцессора, который может стать довольно трудоемким для настройки.
В любом случае, с вашими активными именами атрибутов вы должны назначить им местоположения (и / или семантику), для этого вы один на один со своим вариантом использования.
В нашем движке мы с радостью жестко кодируем местоположения предопределенных имен для конкретных значений, таких как:
glBindAttribLocation(prog, 0, "mg_Position");
glBindAttribLocation(prog, 1, "mg_Color");
glBindAttribLocation(prog, 2, "mg_Normal");
...
После этого пишущий шейдер должен соответствовать предопределенной семантике атрибутов.
AFAIK - это самый распространенный способ, например, OGRE. Это не ракетостроение, но хорошо работает на практике.
Если вы хотите добавить некоторый элемент управления, вы можете предоставить API для определения семантики на основе шейдера, возможно, даже имея это описание в дополнительном файле, который легко разбирается и который расположен рядом с исходным кодом шейдера.
Я не вхожу в униформу, где ситуация почти такая же, за исключением того, что "более новые" расширения позволяют принудительно преобразовывать унифицированные блоки GLSL в структуру памяти, совместимую с вашим приложением.
Я сам не доволен всем этим, поэтому буду рад получить противоречивую информацию:)
Вы можете рассмотреть возможность анализа самого GLSL.
Синтаксис объявления униформы / атрибута довольно прост. Вы можете придумать небольшой ручной парсер, который ищет строки, начинающиеся с uniform
или же attribute
, получить тип и имя, а затем представить некоторые C++ API, используя строки. Это избавит вас от проблем с жестко закодированными именами. Если вы не хотите пачкать руки ручным разбором, пара таких вещей, как Spirit, сделают свое дело.
Вы, вероятно, не захотите полностью анализировать GLSL, поэтому вам нужно убедиться, что вы не делаете ничего смешного в замедлениях, которые могут изменить реальное значение. Одна сложность, которая приходит на ум, - это условная компиляция с использованием макросов в GLSL.