Массивы динамической длины как объекты буфера хранилища шейдеров

Допустим, у меня есть динамическое количество "шаров", к которым я хочу получить доступ в своих шейдерах 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 за подсказку)

Другие вопросы по тегам