3

假设我有一个动态数量的“球”,我想在我的 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 中,我如何指定缓冲区是一个数组,以及我的着色器如何知道这个数组的大小?

4

1 回答 1

1

当使用长度不是编译时间常数的数组时,可以将SSBO 接口块的成员声明为长度未定。

假设存在适合 C++ 球结构的 GLSL 结构,代码可能看起来像这样:

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];
    ...
}

请注意,由于使用 vec3,您当前的 C++ 结构布局可能会导致对齐问题。您可能想阅读我是否应该使用vec3统一缓冲区或着色器存储缓冲区对象的内部?(感谢 Rabbid76 的提示)

于 2019-09-01T09:49:55.833 回答