【问题标题】:Rendering data in OpenGL: Vertices and Compute shaders在 OpenGL 中渲染数据:顶点和计算着色器
【发布时间】:2014-02-16 01:05:59
【问题描述】:

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

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

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

  • 数组缓冲区
  • 纹理(用于存储信息,而不是用于纹理本身)

但是我已经想到并想出了这两种变体的缺点:

数组缓冲区:

  • 我不确定我的计算着色器如何读取,更不用说修改顶点了。但我确实知道如何画它们。

纹理:

  • 我知道如何在计算着色器中修改它们,但是我不确定如何从纹理中绘制。更具体地说,需要绘制的元素数量取决于纹理中写入(数据非零)元素的数量。

我可能忽略了一些满足我需要的重要其他功能,所以真正的问题是:
如何创建驻留在 GPU 上并且我可以在 Vertex 和 Compute Shader 中访问的顶点?

【问题讨论】:

    标签: opengl graphics shader vertex-shader compute-shader


    【解决方案1】:

    希望这将消除一些误解,并让您更好地了解通用着色器存储的设置方式。

    您必须了解缓冲区对象在 GL 中的实际工作方式。你经常听到人们区分“顶点缓冲区对象”“统一缓冲区对象”。实际上,没有根本的区别——缓冲区对象无论存储什么,都以相同的方式处理。它只是一个通用数据存储,只有在绑定到特定点时才具有特殊含义(例如GL_ARRAY_BUFFERGL_UNIFORM_BUFFER)。

    不要考虑驻留在 GPU 上的特殊用途的顶点缓冲区,而是更笼统地考虑 - 它实际上是未格式化的内存,如果您知道结构,您可以读取/写入。像glVertexAttribPointer (...) 这样的调用描述了缓冲区对象的数据结构,足以让glDrawArrays (...) 为每个顶点着色器调用从缓冲区对象的内存中有意义地提取顶点属性。

    您需要自己为计算着色器执行相同的操作,如下所示。您需要熟悉7.6.2.2 - Standard Uniform Block Layout 中讨论的规则才能完全理解以下数据结构。

    使用 Shader Storage Blocks 描述顶点数据结构可以这样完成:

    // 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 布局规则。这意味着使用三分量向量并不总是明智地使用内存。您需要在 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
    

    【讨论】:

    • @skiwi:为了简单起见,我省略了内存屏障的讨论,如果你需要我也可以解释这些。如果您在计算着色器用作另一个着色器阶段(例如顶点着色器)的输入时更新缓冲区的内容,则它们非常重要。
    • 感谢您的解释。我知道您解释的大部分内容,因为我几乎完成了 OpenGL Superbible: 6th Edition,但是您已经成功地阐明了如何“移动”(它实际上仍然停留在同一个缓冲区中) GPU) 图形部分和计算着色器部分之间的数据。
    • 但是在详细的地形上,我可以看到这导致缓冲区中有大量数据需要渲染。实例化不会轻很多吗?如果您能建议如何将其与物理计算联系起来,那就太好了。
    • 这里可以使用std430吗?
    猜你喜欢
    • 2012-08-14
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2016-05-04
    • 2012-09-11
    相关资源
    最近更新 更多