OpenGL 4.5 — хранилище шейдеров: запись в вершинном шейдере, чтение во фрагментном шейдере

И мой фрагментный, и вершинный шейдеры содержат следующих двух парней:

struct Light {
  mat4 view;
  mat4 proj;
  vec4 fragPos;
};

layout (std430, binding = 0) buffer Lights {
  Light lights[];
};

Моя проблема в том, что это последнее поле, fragPos, вычисляется вершинным шейдером следующим образом, но фрагментный шейдер не видит изменения, сделанные вершинным шейдером в fragPos (или вообще какие-либо изменения):

aLight.fragPos = bias * aLight.proj * aLight.view * vec4(vs_frag_pos, 1.0);

... где aLight равно lights[i] в цикле. Как вы понимаете, я вычисляю положение вершины в системе координат каждого присутствующего источника света, чтобы использовать ее в отображении теней. Есть идеи, что здесь не так? Я делаю в корне неправильную вещь?

Вот как я инициализирую свое хранилище:

struct LightData {
  glm::mat4 view;
  glm::mat4 proj;
  glm::vec4 fragPos;
};

glGenBuffers(1, &BBO);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, BBO);

glNamedBufferStorage(BBO, lights.size() * sizeof(LightData), NULL, GL_DYNAMIC_STORAGE_BIT);

// lights is a vector of a wrapper class for LightData.
for (unsigned int i = 0; i < lights.size(); i++) {
  glNamedBufferSubData(BBO, i * sizeof(LightData), sizeof(LightData), &(lights[i]->data));
}

Возможно, стоит отметить, что если я перемещу fragPos в переменную массива out фиксированного размера в вершинном шейдере out fragPos[2], оставлю результаты там, а затем добавлю аналог фрагментного шейдера in fragPos[2] и буду использовать его для остальных моих вещей, тогда все будет в порядке. Итак, о чем я хочу узнать больше, так это о том, почему мой фрагментный шейдер не видит числа, обработанные вершинным шейдером.


person Carlos Romero    schedule 01.08.2017    source источник


Ответы (1)


Я не буду очень точен, но попытаюсь объяснить вам, почему ваш фрагментный шейдер не видит то, что пишет ваш вершинный шейдер:

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

Чтобы избежать этой проблемы, вы должны сделать две вещи: во-первых, вы должны объявить свой буфер как когерентный (внутри glsl): layout(std430) coherent buffer ...

Как только вы это сделаете, после записи вы должны создать барьер (в глобальном масштабе он говорит: будьте осторожны, я записываю значение в буфер, значения, которые вы будете читать, могут быть недействительными, пожалуйста, возьмите новые значения, которые я написал).

Как сделать подобное? Использование функции memoryBarrierBuffer после ваших записей. https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/memoryBarrierBuffer.xhtml

Кстати: не забудьте разделить на w после вашей проекции.

person Antoine Morrier    schedule 03.08.2017
comment
Эй, спасибо за ваш ответ. Это похоже на то, чего мне не хватает. Я обязательно попробую сегодня позже. - person Carlos Romero; 03.08.2017
comment
хм как-то я не могу заставить его работать. Я предполагаю, что вызов memoryBarrierBuffer должен идти в моем вершинном шейдере после того, как я закончу писать, из того, что говорит мне красная книга? - person Carlos Romero; 03.08.2017
comment
Обычно да, например: buffer.value = x; памятьБарьерБуфер - person Antoine Morrier; 03.08.2017
comment
Кроме того, подумайте об условиях вашей гонки. Это могут быть тысячи вершин, которые обрабатываются одновременно. Таким образом, возможно, что ваши значения перезаписаны, и это объясняет, почему вы не получаете хорошее значение;) - person Antoine Morrier; 03.08.2017
comment
хм, я думал, что memoryBarrierBuffer защитит меня от этого. Я также читаю, что этот метод работает с атрибутами image<type>. Я пытаюсь изменить fragPos на image1D и использовать imageLoad и imageStore для чтения/записи в него. - person Carlos Romero; 03.08.2017
comment
Для изображения вам нужно использовать memoryBarrierImage. Барьер говорит только о том, что ваши записи будут видны другим потокам. Это, к счастью для выступлений, не мьютекс или что-то в этом роде - person Antoine Morrier; 03.08.2017