Явная и автоматическая привязка расположения атрибутов для шейдеров OpenGL

83

При настройке расположения атрибутов для программы шейдера OpenGL вы сталкиваетесь с двумя вариантами:

glBindAttribLocation () перед связыванием, чтобы явно определить местоположение атрибута.

или же

glGetAttribLocation () после связывания, чтобы получить автоматически назначенное местоположение атрибута.

Какая утилита позволяет использовать одно поверх другого?

И какой из них предпочтительнее на практике?

Цзин
источник
6
Я не стал использовать glBindAttribLocationсвой графический движок, который отлично работал в Linux. Когда я портировал в окна, он использовал мои нормали в качестве вершин - мне пришлось явно указать ему порядок переменных в моем шейдере, glBindAttribLocationчтобы заставить его работать ...
Джарретт

Ответы:

93

Я знаю одну вескую причину предпочесть явное определение местоположения.

Учтите, что вы храните свои геометрические данные в объектах Vertex Array Objects. Для данного объекта вы создаете VAO таким образом, чтобы индексы соответствовали, например:

  • индекс 0 : позиции,
  • индекс 1 : нормали,
  • индекс 2 : texcoords

Теперь представьте, что вы хотите нарисовать один объект двумя разными шейдерами . Один шейдер требует в качестве входных данных позиции и нормальных данных , другой - позиции и координаты текстуры .

Если вы скомпилируете эти шейдеры, вы заметите, что первый шейдер будет ожидать позиции с индексом атрибута 0 и нормали с индексом 1. Другой будет ожидать позиций с 0, а координаты текстуры с 1.

Цитата https://www.opengl.org/wiki/Vertex_Shader :

Автоматическое присвоение

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

Это означает, что вы не сможете использовать свой VAO с обоими шейдерами. Вместо того, чтобы иметь один VAO для, скажем, объекта, вам понадобится - в худшем случае - отдельный VAO для каждого объекта на шейдер .

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

(На самом деле это не большая проблема, если вы не используете отдельные VAO, но все же может сделать ваш код более понятным.)


Кстати:

При настройке расположения атрибутов для программы шейдера OpenGL вы сталкиваетесь с двумя вариантами

В OpenGL / GLSL 3.3 есть третий вариант: указать местоположение непосредственно в коде шейдера . Выглядит это так:

layout(location=0) in vec4 position;

Но этого нет в языке шейдеров GLSL ES.

Кос
источник
1
Я не вижу в этом смысла. VAO действительно легкие, и вы обычно воссоздаете их для каждого кадра. В вашем случае вы бы просто создали разные VAO перед вызовом каждого шейдера, не так ли?
PierreBdR 08
1
Да, конечно, вы можете это сделать. Это будет работать так же хорошо, мы обсуждаем здесь только условности. :)
Кос
4
Третий вариант фактически доступен в GLES2.0, но формат немного отличается: layout (location = 0) attribute vec4 position; Обратите внимание, что вам также понадобится это в верхней части вашего файла GLSL: #extension GL_EXT_separate_shader_objects: enable
Гэвин Маклин
35
«VAO действительно легковесны, и вы обычно воссоздаете их для каждого кадра. В вашем случае вы бы просто создали разные VAO перед вызовом каждого шейдера, не так ли?» - Нет, обычно вы этого не делаете, поскольку это в конечном итоге полностью сводит на нет всю цель VAO. Зачем тогда вообще использовать VAO?
Christian Rau
2
Просто примечание. Вы бы не сохранили свои геометрические данные в VAO. Это было бы в VBO. VAO не имеют хранилища данных.
средний Джо
20

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

Litherum
источник
Спасибо, это очень важное соображение.
Jing
1
Насколько я понимаю, в большинстве ситуаций вам все равно придется позвонить glGetUniformLocation; актуально ли это конкретное соображение?
Майк Вейр,
@MikeWeir, начиная с GL 4.3, вы можете указать явное единое местоположение .
Руслан
1
@Ruslan Они используют OpenGL ES.
Майк Вейр,
9

Третий вариант, то есть layout(location=0) in vec4 position;в коде шейдера, теперь доступен в OpenGL ES 3.0 / GLSL 300 es. Но только для входных переменных вершинного шейдера.

полоса дороги
источник
7
Как они намекают, карты Intel могут не поддерживать это (хотя в остальном они идеально соответствуют версии 3.0+, насколько я понимаю), что отвратительно :)
mlvljr 09