В своем приложении я добавляю два источника света. Один в (0,0,2), а второй в (2,0,0). Вот что я получаю (оси x, y, z представлены соответственно красной, зеленой и синей линиями):
Обратите внимание, как работает только первый свет, а второй нет. Я сделал свой профиль ядра приложения совместимым, чтобы проверять буферы с помощью различных инструментов, таких как RenderDoc и NSight, и оба показывают мне, что данные второго источника света присутствуют в буфере (снимок сделан во время работы Nsight):
Позиции вроде правильно перенесены в буфер памяти 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 байтам, и мы видим, что первый источник света работает нормально. Почему второй раз обновление / перераспределение не работает?