在OpenGL中渲染数据:顶点和计算着色器

ski*_*iwi 6 opengl graphics shader compute-shader vertex-shader

我希望能够将一堆顶点输入到我的图形程序中,然后我希望能够对它们执行以下操作:

  • 在OpenGL的图形部分中使用它们,尤其是在顶点着色器中.
  • 在Compute Shader中对它们进行物理计算.

根据这些要求,我认为我需要一些结构来存储我的顶点并且可以正确访问它们,我想到了以下内容:

  • ArrayBuffers
  • 纹理(如存储信息,而不是纹理本身)

然而,我已经想到并提出了两种变体的缺点:

ArrayBuffers:

  • 我不确定我的Compute Shader如何读取,更不用说修改顶点了.但我确实知道如何绘制它们.

纹理:

  • 我知道如何在Compute Shaders中修改它们,但我不确定如何从纹理中绘制.更具体地,需要绘制的元素的数量取决于纹理中的写入(数据非零)元素的数量.

我可能忽略了一些满足我需要的重要其他功能,因此真正的问题是:
如何创建驻留在GPU上的顶点以及我可以在顶点和计算着色器中访问哪些?

And*_*man 11

希望这将消除一些误解,并让您更好地了解如何设置通用着色器存储.

您需要了解的是缓冲区对象在GL中的实际工作方式.您经常听到人们区分"顶点缓冲区对象""统一缓冲区对象"之类的东西.实际上,没有根本的区别 - 无论存储什么,缓冲对象都以相同的方式处理.它只是一个通用数据存储,只有在绑定到特定点(例如GL_ARRAY_BUFFERGL_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 [];
};
Run Code Online (Sandbox Code Playgroud)

这允许您在计算着色器中使用交错的顶点缓冲区,其中包含上面定义的数据结构.当你这样做时,你必须小心数据对齐...你可能会偶然使用你想要的任何对齐/步幅通常交错的顶点数组,但在这里你要符合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
Run Code Online (Sandbox Code Playgroud)