GLSL - невозможно получить доступ ко второму индексу массива SSBO для нескольких огней

В своем приложении я добавляю два источника света. Один в (0,0,2), а второй в (2,0,0). Вот что я получаю (оси x, y, z представлены соответственно красной, зеленой и синей линиями):

куб, в котором работает только одна лампа

Обратите внимание, как работает только первый свет, а второй нет. Я сделал свой профиль ядра приложения совместимым, чтобы проверять буферы с помощью различных инструментов, таких как RenderDoc и NSight, и оба показывают мне, что данные второго источника света присутствуют в буфере (снимок сделан во время работы Nsight):

nsight capture

Позиции вроде правильно перенесены в буфер памяти gpu. Вот реализация моего фрагментного шейдера, который использует SSBO для обработки нескольких источников света в моем приложении:

#version 430

struct Light {
  vec3  position;
  vec3  color;
  float intensity;
  float attenuation;
  float radius;
};

layout (std140, binding = 0) uniform CameraInfo {
  mat4  ProjectionView; 
  vec3  eye;
};

layout (std430, binding = 1) readonly buffer LightsData {
    Light lights[];
};

uniform vec3  ambient_light_color;
uniform float ambient_light_intensity;

in  vec3 ex_FragPos;
in  vec4 ex_Color;
in  vec3 ex_Normal;
out vec4 out_Color;

void main(void)
{
    // Basic ambient light
    vec3 ambient_light = ambient_light_color * ambient_light_intensity;

    int i;
    vec3 diffuse = vec3(0.0,0.0,0.0);
    vec3 specular = vec3(0.0,0.0,0.0);
    for (i = 0; i < lights.length(); ++i) {
        Light wLight = lights[i];
        // Basic diffuse light
        vec3 norm = normalize(ex_Normal); // in this project the normals are all normalized anyway...
        vec3 lightDir = normalize(wLight.position - ex_FragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        diffuse += diff * wLight.color;

        // Basic specular light
        vec3 viewDir = normalize(eye - ex_FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);  
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
        specular += wLight.intensity * spec * wLight.color;  
    }

    out_Color = ex_Color * vec4(specular + diffuse + ambient_light,1.0); 
}

Обратите внимание, что я прочитал раздел 7.6.2.2 OpenGL 4.5 spec и что, если я правильно понял, мое выравнивание должно соответствовать размеру самого большого члена моей структуры, которым является vec3, а размер моей структуры составляет 36 байтов, поэтому здесь все должно быть в порядке. Я также пробовал другую версию std (например, std140) и добавил немного отступов, но ничего не решило проблему со вторым светом. В моем коде на C ++ у меня есть эти определения для добавления источников света в мое приложение:

light_module.h / .cc:

struct Light {
  glm::f32vec3  position;
  glm::f32vec3  color;
  float         intensity;
  float         attenuation;
  float         radius;
};
...
constexpr GLuint        LIGHTS_SSBO_BINDING_POINT = 1U;
std::vector<Light>      _Lights;
...
void AddLight(const Light &light) {
  // Add to _Lights
  _Lights.push_back(light);
UpdateSSBOBlockData(
  LIGHTS_SSBO_BINDING_POINT, _Lights.size()* sizeof(Light),
  static_cast<void*>(_Lights.data()), GL_DYNAMIC_DRAW);
}

shader_module.h / .cc:

using SSBOCapacity = GLuint;
using BindingPoint = GLuint;
using ID = GLuint;
std::map<BindingPoint, std::pair<ID, SSBOCapacity> >  SSBO_list;
...
void UpdateSSBOBlockData(GLuint a_unBindingPoint,
  GLuint a_unSSBOSize, void* a_pData, GLenum a_eUsage) {
  auto SSBO = SSBO_list.find(a_unBindingPoint);
  if (SSBO != SSBO_list.end()) {
    GLuint unSSBOID = SSBO->second.first;
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, unSSBOID);
    glBufferData(GL_SHADER_STORAGE_BUFFER, a_unSSBOSize, a_pData, a_eUsage);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); //unbind
  }
  else 
    // error handling...
}

По сути, я пытаюсь обновить / перераспределить размер SSBO с помощью glBufferData каждый раз, когда в моем приложении добавляется свет.

Теперь, поскольку у меня возникли проблемы с обработкой данных второго источника света, я изменил свой код шейдера фрагмента, чтобы он выполнял только второй источник света в моем массиве SSBO, заставляя i = 1 и выполняя цикл до i < 2, но я получаю следующие ошибки:

(50) : error C1068: ... or possible array index out of bounds
(50) : error C5025: lvalue in field access too complex
(56) : error C1068: ... or possible array index out of bounds
(56) : error C5025: lvalue in field access too complex

Строки 50 и 56 относятся к diffuse += diff * wLight.color; и specular += wLight.intensity * spec * wLight.color; соответственно. Есть ли действительно доступ за границу, даже если я добавлю свои источники света до первого вызова отрисовки? Почему шейдер компилируется правильно, если я использую lights.length() вместо 2?

Наконец, я добавил простой if (i == 1) в свой цикл for, чтобы проверить, равно ли lights.length () 2, но он не входит в него. Тем не менее, начальный размер моего буфера равен 0, а затем я добавляю источник света, который устанавливает размер буфера равным 36 байтам, и мы видим, что первый источник света работает нормально. Почему второй раз обновление / перераспределение не работает?


person pandaman1234    schedule 31.10.2017    source источник


Ответы (1)


Итак, я добавил немного отступов в конце объявления моей структуры только на стороне C ++. Требуемое заполнение - float [3] или 12 байтов, что в сумме составляет 48 байтов. Я все еще не уверен, почему это необходимо, поскольку в спецификации указано (как указано в этот пост)

  1. Если элемент является структурой, базовое выравнивание структуры равно N, где N - наибольшее базовое значение выравнивания любого из его элементов, округленное до базового выравнивания vec4. Затем отдельным элементам этой подструктуры назначаются смещения путем рекурсивного применения этого набора правил, где базовое смещение первого члена подструктуры равно выровненному смещению структуры. Структура может иметь заполнение в конце; базовое смещение элемента, следующего за подструктурой, округляется в большую сторону до следующего кратного базового выравнивания конструкции. [...]

При использовании макета хранения std430 блоки хранилища шейдеров будут размещены в буферном хранилище идентично унифицированным и шейдерным блокам хранилища с использованием макета std140, за исключением того, что базовое выравнивание и шаг массивов скаляров и векторов в правиле 4 и структур в правиле 9 не округляются до значения, кратного базовому выравниванию vec4.

Я предполагаю, что такие структуры, как vec3 и glm :: f32vec3, определенные glm, рекурсивно округляются до vec4 при использовании std430, и поэтому моя структура должна следовать выравниванию vec4. Если кто-нибудь может подтвердить это, это было бы интересно, поскольку связанный пост выше касается напрямую vec4, а не vec3.

Картинка с двумя работающими лампочками:

второй свет работает

РЕДАКТИРОВАТЬ:

После дополнительных исследований выясняется, что последние 3 поля структуры Light (интенсивность, затухание и радиус) не использовались. Я исправил это, изменив положение и цвет с glm :: f32vec3 на glm :: vec4. Дополнительную информацию можно найти в похожий пост. Я также оставил один поплавок для заполнения из-за выравнивания, упомянутого ранее.

person pandaman1234    schedule 01.11.2017