Массивы динамической длины как объекты буфера хранилища шейдеров
Допустим, у меня есть динамическое количество "шаров", к которым я хочу получить доступ в своих шейдерах OpenGL. В C++ данные могут быть такими:
struct Ball
{
glm::vec3 position;
glm:vec3 colour;
float size;
};
std::vector<Ball> all_balls;
Если я хочу повторить all_balls
Я считаю, что в моем фрагментном шейдере мне понадобится буферный объект хранилища шейдеров.
Эта документация касается массивов, но особенно неполна.
Я предполагаю, что могу отправить данные в буфер вот так
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, all_balls.size() * sizeof(Ball), &(all_balls[0]), usage);
Как в GLSL указать, что буфер является массивом, и как мой шейдер узнает размер этого массива?
1 ответ
При работе с массивом, длина которого не является постоянной времени компиляции, можно объявить член блока интерфейса SSBO неопределенной длины.
Предполагая, что существует структура GLSL, подходящая для структуры шара C++, код может выглядеть примерно так:
struct GLSLBall {...};
layout(std430, binding = 0) buffer BallBuffer
{
GLSLBall ball_data[];
}
Вы можете использовать итератор по всем элементам следующим образом:
for (int i = 0; i < ball_data.length(); ++i)
{
GLSLBall currentBall = ball_data[i];
}
Когда количество элементов меняется очень часто, я предлагаю не изменять размер / перераспределять SSBO каждый раз, а зарезервировать достаточно большой буфер один раз и передать количество элементов, фактически используемых в шейдер. Это может быть независимая равномерная переменная (uniform uint ballCount;
), или вы можете упаковать его в сам SSBO вот так:
struct GLSLBall {...};
layout(std430, binding = 0) buffer BallBuffer
{
uint ball_length;
GLSLBall ball_data[];
}
Тогда вы можете выделить память только один раз:
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, ENOUGH_MEMORY_FOR_ALL_CASES, null, usage);
и загружать данные каждый раз, когда содержимое изменяется следующим образом:
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(unsigned int), (unsigned int)all_balls.size());
glBufferSubData(GL_SHADER_STORAGE_BUFFER, sizeof(unsigned int), all_balls.size() * sizeof(Ball), &(all_balls[0]));
Тогда цикл glsl похож на
for (int i = 0; i < BallBuffer.length; ++i)
{
GLSLBall currentBall = ball_data[i];
...
}
Обратите внимание, что текущий макет структуры C++ может вызвать некоторые проблемы с выравниванием из-за использования vec3. Вы можете прочитать " Стоит ли мне когда-нибудь использоватьvec3
внутри унифицированного буфера или буферного объекта хранилища шейдеров?(спасибо Rabbid76 за подсказку)