2013-04-05 2 views
3

Я видел здесь несколько вопросов и ответов о переполнении стека по этой теме. Из этих ответов я придумал возможное решение привязать атрибуты GLSL к пользовательской семантике. Я хотел получить информацию и обсудить ее, и проверить, была ли это даже правильная идея.GLSL - Атрибуты привязки к семантике

Для начала, давайте предположим, что мы имеем некоторый список определяемых пользователем семантики:

enum VertexElementSemantic 
{ 
    POSITION, NORMAL, AMBIENT, DIFFUSE, SPECULAR, 
    TEX_COORD0, TEX_COORD1, TEX_COORD2, TEX_COORD3, 
    INDICES 
}; 

И структура, которая инкапсулирует данные, необходимые для установки указателя атрибутов вершин.

struct VertexElement 
{ 
    unsigned int m_source; 
    unsigned int m_offset; 
    unsigned int m_stride; 
} 

Теперь некоторые RenderOperation класс будет содержать карту VertexElementSemantics к VertexElements. Формат, размер и нормализация VertexElement могут определяться его семантикой.

Последний бит информации, который нам требуется для установки этого указателя, - это местоположение самого атрибута. Здесь мы хотим привязать наш VertexElementSemantic к определенному местоположению.

С the first answer to this question, мы узнаем, что мы можем явно указать желаемое местоположение каждого атрибута следующим образом:

layout(location = 0) in vec3 position; 

Таким образом, мы могли карту нашей семантики этих трудно закодированные места, но мы требуем этого места быть жестко закодированным в каждом шейдере. Любые изменения в этих местоположениях требуют от нас пройти и редактировать каждый шейдер.

Однако это значение необязательно должно предоставляться источником шейдера. Из the answer to this question, мы узнаем, что мы можем внешне добавить #defines нашего Shaders так:

char *sources[2] = { "#define FOO\n", sourceFromFile }; 
glShaderSourceARB(shader, 2, sources, NULL); 

Используя это, мы могли бы построить строку, #defines переменных для нужных мест каждого семантические. Например, мы могли бы построить строку, которая будет в конечном итоге вставить следующий код в начало каждого из наших Shaders:

#define POSITION_LOCATION 0 
#define NORMAL_LOCATION 1 
#define AMBIENT_LOCATION 2 
... 

Возвращаясь к явному указанию наших местоположений атрибутов, мы должны быть в состоянии изложить их в качестве таковых в настоящее время :

layout(location = POSITION_LOCATION) in vec3 position; 
layout(location = NORMAL_LOCATION) in vec3 normal; 
layout(location = AMBIENT_LOCATION) in vec4 ambient; 

Этот метод позволит нам установить желаемое расположение атрибутов каждого семантика в коде. Он также предоставляет своеобразное семантическое связывание с самими шейдерами. Является ли система подобным образом шагом в правильном направлении для решения вопроса о предоставлении смысла местам атрибутов?

+0

«Любые изменения в этих местоположениях требуют, чтобы мы проходили и редактировали каждый шейдер.» «Тогда не меняйте их. Это произвольные числа; само фактическое число совершенно не имеет значения. Одно число в конечном счете не лучше, чем другое, поэтому нет причин назначать им разные числа. –

+2

"* Из ответа на этот вопрос мы узнаем, что мы можем внешне добавлять #defines к нашим шейдерам следующим образом: *« Спасибо, что сообщили мне об этом ответе, чтобы я мог *** уменьшить его *** и предупредить другие не делают этого. –

ответ

4

Давайте рассмотрим последствия этой идеи.

Мы могли бы построить строку, которая #defines переменные для желаемых мест каждой семантики. Например, мы могли бы построить строку, которая в конечном итоге добавит следующее к началу каждого из наших шейдеров:

Ну, это плохо по двум причинам. Во-первых, есть проблема #version. Если вы хотите использовать любой GLSL версия, отличная от 1.10, вы должны предоставить объявление #version. И это заявление должно быть Первое, что в шейдере, вне комментариев и пробелов.

Поместив эти #define s в источник шейдера (будь то путем конкатенации строк или с помощью нескольких строк, как вы это делаете), вы должны принять определенные последствия. Обычно каждый отдельный файл шейдера будет иметь свою собственную декларацию #version, указав, какую версию GLSL он использует. Но вы не можете этого сделать, если хотите использовать что-то помимо GLSL 1.10. У вас должен быть исходный код C++ сгенерировать #version, перед вашим #define.

Это означает, что ваш источник шейдера теперь отсоединен от версии, которую он компилирует под. Это выполнимо, но это означает, что ваш шейдерный источник теперь неясен, не зная, какая версия. Вы могли бы сообщить версию другим способом, например, с именем файла (например, lit_transform_330.vert будет использовать версию 3.30). Но вам придется разработать такую ​​систему.

Теперь, когда проблема с версией разобралась, к следующей проблеме: то, что вы делаете, это redundant.

Вы используете термины типа «семантический», которые не имеют никакого значения для OpenGL. Похоже, вы пытаетесь присвоить некоторую форму имени определенному атрибуту вершины, чтобы вы могли видеть использование этого имени в вашем шейдере и на C++-коде и, следовательно, знаете, для чего он предназначен.

То есть вы хотите определить сопоставление между «именем» и «индексом атрибута». Вы хотите, чтобы он определялся в одном месте, чтобы он автоматически распространялся на каждый шейдер и использовался последовательно во всем исходном коде C++.

Ну, у нас уже есть сопоставление между именем и индексом атрибута. Он называется «сопоставление между именем атрибута и индексом атрибута». Каждый шейдер должен предоставить имя для своих атрибутов. Это имя строки, которое вы видите в таких определениях, как in vec4 position;, имя атрибута - position. Это то, что GLSL вызывает эту переменную, когда она ее использует.

Как указано в ответе, с которым вы связаны, вы можете связать определенное имя атрибута с индексом атрибута из кода C++ до того, как программа будет связана. Это делается через the glBindAttribLocation function. Вы можете установить любое количество сопоставлений, которые вам нравятся. Когда программа связана, атрибутам, которые соответствуют указанному местоположению, будет присвоено это местоположение.

Все, что вам нужно, это список ваших «семантик» (ака: индексы атрибутов) и имена строк, которым нужны шейдеры для использования для этих атрибутов.

Вы можете сказать: «Ну, я хочу, чтобы шейдеры имели свободу называть переменную независимо от того, что хотите». Мой ответ был бы ... в чем разница? Ваша предлагаемая схема уже требует от пользователя соблюдения определенного соглашения об именах. Просто имя, которое они должны использовать, не является именем переменной; это имя некоторого тега, который вы связываете с переменной во время объявления.

Так в чем же разница? Что автор шейдера должен придерживаться схемы присвоения имен для имени переменной атрибута вершины? Разве это не согласованное имя для одной и той же концепции во всех шейдерах? хорошая вещь?

Единственное отличие состоит в том, что если они ошибочно принимают «семантику» по вашей схеме, они получают ошибку компиляции шейдера (поскольку их ошибочное «семантическое» имя не будет соответствовать никаким фактическим #define). Если они ошибочно называют имя атрибута, они получат ошибку компилятора, если они не ошибочно используют это имя при использовании этого атрибута.

Есть способы поймать это. Для этого необходимо использовать program introspection, чтобы просмотреть список активных атрибутов и проверить их на имена ожидающих атрибутов.

Вы можете сварить это до очень простого набора условностей. Используя ваше «семантическое» определение:

enum VertexElementSemantic 
{ 
    POSITION, NORMAL, AMBIENT, DIFFUSE, SPECULAR, 
    TEX_COORD0, TEX_COORD1, TEX_COORD2, TEX_COORD3, 
    INDICES, NUM_SEMANTICS 
}; 

//in the C++ file you use to link your shaders 
const char *AttributeNames[] = 
{ 
    "position", "normal", "ambient", "diffuse", "specular", 
    "tex_coord0", "tex_coord1", "tex_coord2", "tex_coord3", 
    "indices", 
} 

static_assert(ARRAY_COUNT(AttributeNames) == NUM_SEMANTICS); //Where `ARRAY_COUNT` is a macro that computes the number of elements in a static array. 

GLuint CreateProgram(GLuint vertexShader, GLuint fragmentShader) 
{ 
    GLuint prog = glCreateProgram(); 
    //Attach shaders 
    for(int attrib = 0; attrib < NUM_SEMANTICS; ++attrib) 
    { 
    glBindAttribLocation(prog, attrib, AttributeNames[attrib]); 
    } 

    glLinkProgram(prog); 

    //Detach shaders 
    //Check for linking errors 

    //Verify that attribute locations are as expected. 
    //Left as an exercise for the reader. 

    return prog; 
} 

Лично я бы просто использовал число. Независимо от того, что вы используете, человек, пишущий шейдер, должен будет придерживаться какой-либо конвенции. Это означает, что когда они отправляются писать вершинный шейдер, который занимает позицию, им придется искать, как сказать «это позиция». Так что им нужно будет посмотреть что-то вверх в таблице где-нибудь, что бы ни было.

В какой момент это сводится к наиболее вероятной проблеме. Наиболее вероятными проблемами могут быть либо те, кто считает, что они знают ответ, но на самом деле ошибочны (т. Е. Не искали), и кто-то, кто ошибся в ответе. Очень сложно ошибиться в количестве (хотя это, безусловно, может случиться), в то время как намного легче ошибиться POSITION_LOCATION. Первая проблема может произойти с одним или более равным числом.

Так что мне кажется, что вы, скорее всего, получите меньше проблем с неправильным совпадением, если ваше соглашение основано на числах, а не на словах.

+4

Большое спасибо за быстрый ответ. Я не знал, что #version должен быть первой строкой шейдера. Оглядываясь назад, я действительно ничего не делал, как просто форсировать имена атрибутов. –

Смежные вопросы