我希望能够向我的图形程序输入一堆顶点,然后我希望能够对它们执行以下操作:
- 在OpenGL的图形部分使用它们,特别是在顶点着色器中。
- 在计算着色器中对它们进行物理计算。
通过这些要求,我认为我需要一些结构来存储我的顶点并可以正确访问它们,我想到了以下内容:
- 数组缓冲区
- 纹理(如存储信息,而不是用于纹理本身)
但是,我想过并提出了两种变体的缺点:
数组缓冲区:
- 我不确定我的计算着色器如何读取顶点,更不用说修改顶点了。然而,我确实知道如何画它们。
纹理:
我- 知道如何在计算着色器中修改它们,但是我不确定如何从纹理中绘制。更具体地说,需要绘制的元素数量取决于纹理中写入(数据不为零)元素的数量。
我可能忽略了一些满足我需求的重要其他功能,因此真正的问题是:
如何创建驻留在 GPU 上的顶点,并且可以在顶点和计算着色器中访问它们?
希望这将消除一些误解,并让您更好地了解通用着色器存储的设置方式。
你必须了解的是缓冲区对象在GL中是如何工作的。您经常听到人们区分"顶点缓冲区对象"和"统一缓冲区对象">之类的东西。实际上,没有根本的区别 - 无论缓冲区对象存储什么,它都以相同的方式处理。它只是一个通用数据存储,只有在绑定到特定点时才具有特殊含义(例如GL_ARRAY_BUFFER
或GL_UNIFORM_BUFFER
)。
不要考虑驻留在 GPU 上的特殊用途顶点缓冲区,更笼统地考虑 - 如果您知道结构,它实际上是可以读/写的未格式化内存。像glVertexAttribPointer (...)
这样的调用充分描述了缓冲区对象的数据结构,以便glDrawArrays (...)
从缓冲区对象的内存中有意义地提取顶点属性以进行每个顶点着色器调用。
对于计算着色器,您需要自己执行相同的操作,如下所示。您需要熟悉7.6.2.2 - 标准统一块布局中讨论的规则,以充分理解以下数据结构。
使用着色器存储块的顶点数据结构的描述可以这样完成:
// Compute Shader SSB Data Structure and Buffer Definition
struct VtxData {
vec4 vtx_pos; // 4N [GOOD] -- Largest base alignment
vec3 vtx_normal; // 3N [BAD]
float vtx_padding7; // N (such that vtx_st begins on a 2N boundary)
vec2 vtx_st; // 2N [BAD]
vec2 vtx_padding10; // 2N (in order to align the entire thing to 4N)
}; // ^^ 12 * sizeof (GLfloat) per-vtx
// std140 is pretty important here, it is the only way to guarantee the data
// structure is aligned as described above and that the stride between
// elements in verts[] is 0.
layout (std140, binding = 1) buffer VertexBuffer {
VtxData verts [];
};
这允许您使用上面定义的数据结构在计算着色器中使用交错顶点缓冲区。执行此操作时,您必须小心数据对齐...通常,您可以随意使用交错顶点数组所需的任何对齐/步幅,但在这里您希望符合std140
布局规则。这意味着使用 3 分量向量并不总是明智地使用内存;您需要在N(float
),2N(vec2
)或4N(vec3
/vec4
)边界上对齐,这通常需要插入填充和/或巧妙地打包数据。在上面的示例中,您可以在对齐填充所浪费的所有空间中拟合整个 3 分量向量的数据。
显示如何创建和绑定缓冲区以供两用的伪代码:
struct Vertex {
GLfloat pos [4];
GLfloat normal [3];
GLfloat padding7;
GLfloat st [2];
GLfloat padding10 [2];
} *verts;
[... code to allocate and fill verts ...]
GLuint vbo;
glGenBuffers (1, &vbo);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glBufferData (GL_ARRAY_BUFFER, sizeof (Vertex) * num_verts, verts, GL_STATIC_DRAW);
glVertexAttribPointer (0, 4, GL_FLOAT, GL_FALSE, 48, 0); // Vertex Attrib. 0
glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 48, 16); // Vertex Attrib. 1
glVertexAttribPointer (2, 2, GL_FLOAT, GL_FALSE, 48, 32); // Vertex Attrib. 2
glBindBufferBase (GL_SHADER_STORAGE_BUFFER, 1, vbo); // Buffer Binding 1