为啥在向着色器存储缓冲区发送结构时会收到垃圾数据?

Posted

技术标签:

【中文标题】为啥在向着色器存储缓冲区发送结构时会收到垃圾数据?【英文标题】:Why do I got junk data, when sending a structure to shader storage buffer?为什么在向着色器存储缓冲区发送结构时会收到垃圾数据? 【发布时间】:2020-05-09 14:50:42 【问题描述】:

我正在使用 c++ 开发基于 opengl 的光线追踪器应用程序。我想将数据从 cpu 端发送到片段着色器。数据是包围体层次结构 (BVH) 树。 不幸的是,在着色器方面,当我尝试使用 gl_fragcolor 将坐标写为颜色时,我得到了垃圾数据。我不知道为什么。任何帮助表示赞赏。

我在 CPU 端有这个结构:

struct FlatBvhNode 

        glm::vec4 min;   
        glm::vec4 max;
        int order;
        bool isLeaf;
        bool createdEmpty;
        vector<glm::vec4> indices;

        FlatBvhNode()  

        FlatBvhNode(glm::vec3 min, glm::vec3 max, int ind, bool isLeaf, bool createdEmpty, vector<glm::vec3> indices) 
            this->min=glm::vec4(min.x, min.y, min.z, 1.0f);
            this->max=glm::vec4(max.x, max.y, max.z, 1.0f);
            this->order=ind;
            this->isLeaf=isLeaf;
            this->createdEmpty=createdEmpty;

            indices.reserve(2);
            for (int i = 0; i < indices.size(); i++) 
                this->indices.push_back(glm::vec4(indices.at(i).x, indices.at(i).y, indices.at(i).z, 1));
            
        
    ;

我想在片段着色器中接收上述结构:

struct FlatBvhNode     //base aligment     aligned offset
    vec3 min;           // 16 byte          0
    vec3 max;           // 16 byte          16
    int order;          // 4 byte           20
    bool isLeaf;        // 4 byte           24
    bool createdEmpty;  // 4 byte           28
    vec3 indices[2];    // 32 byte          32
;

我不确定对齐的偏移是否正确。我读过 vec3s 被视为 16 个字节,布尔值和整数被视为 4 个字节。其次,对齐的偏移量必须等于或倍于基本对齐。

我用这些代码发送上述结构:

    vector<BvhNode::FlatBvhNode>* nodeArrays=bvhNode.putNodeIntoArray();
    unsigned int nodesArraytoSendtoShader;
    glGenBuffers(1, &nodesArraytoSendtoShader);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, nodesArraytoSendtoShader);

    glBufferData(GL_SHADER_STORAGE_BUFFER, nodeArrays->size() * sizeof(BvhNode::FlatBvhNode),  nodeArrays, GL_STATIC_DRAW);
    glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, nodesArraytoSendtoShader, 0, nodeArrays->size() * sizeof(BvhNode::FlatBvhNode));
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);

【问题讨论】:

vector&lt;glm::vec4&gt; indices; 是一个包含指向动态内存的指针的对象。你必须使用std::array。在 c++ 代码中,数据类型为 std::vec4。在着色器中,类型为vec3。请阅读Should I ever use a vec3 inside of a uniform buffer or shader storage buffer object? ...阅读OpenGL 4.6 API Core Profile Specification; 7.6.2.2 Standard Uniform Block Layout 我相信你,std::array 更相关,但是,我在另一种情况下使用 std::vector,一切似乎都很好,除了一件事:在 cpu 方面我必须使用vec4 和 gpu 端 vec3s 不,你错了。 vector&lt;glm::vec4&gt; indices; 的内容不是你所期望的。使用调试器调查内存。当结构是缓冲区的元素时,您可以将std::vector 用于缓冲区,但不能用于结构中的元素。请学习基础知识。 好的,我明白了。我会检查基础知识。 【参考方案1】:

当您指定Shader Storage Buffer Object 时,您必须使用std430 限定符。 见OpenGL 4.6 API Core Profile Specification; 7.6.2.2 Standard Uniform Block Layout。

如果要在着色器中使用如下数据结构:

struct FlatBvhNode
     
                        // base aligment    aligned offset
    vec4 min;           // 16 byte          0
    vec4 max;           // 16 byte          16
    int  order;         // 4 byte           32
    int  isLeaf;        // 4 byte           36
    int  createdEmpty;  // 4 byte           40
    vec4 indices[2];    // 32 byte          48
;

layout(std430) buffer TNodes

    FlatBvhNode nodes[];

那么你必须考虑,indices 是一个vec4 类型的数组。因此indices 对齐到 16 个字节。

这对应于c++中的如下数据结构

struct FlatBvhNode 

        glm::vec4 min;   
        glm::vec4 max;
        int order;
        int isLeaf;
        int createdEmpty;
        int dummy; // alignment
        std::array<glm::vec4, 2> indices;

注意,std::vector 封装了动态大小的数组,std::array 封装了固定大小的数组。 std::array&lt;glm::vec4, 2&gt; 对应于glm::vec4[2],但std::vector&lt;glm::vec4&gt; 更像glm::vec4* 以及一些额外的尺寸信息。

【讨论】:

“当你指定然后你必须使用”句子中缺少的东西。顺便说一句,谢谢你的帮助! 我尝试了您的解决方案。一切都很好,除了布尔值。当我写 FragColor = vec4(nodes[0].isLeaf, 1, 1, 1);要获得青色或白色,无论 isLeaf 是真还是假,它总是给我白色。但其他变量都很好。 @Tom 我建议使用int 而不是bool(两边)。

以上是关于为啥在向着色器存储缓冲区发送结构时会收到垃圾数据?的主要内容,如果未能解决你的问题,请参考以下文章

WebGL 3D 入门系列 --- 绘制渐变三角形:深入理解缓冲区

WebGL简易教程:向着色器传输数据

绘制彩色立方体,使用attribute变量向着色器传值,BufferGeometry和ShaderMaterial配合使用(three.js实战6)

GLSL 共享深度缓冲区似乎不起作用

为啥当我使用 nativescript 从 iOS 发送表情符号字符时会收到陌生字符串

为啥电脑已提示邮件发送成功而对方却没收到?