OpenGL glBufferSubData - 无法让它工作

Posted

技术标签:

【中文标题】OpenGL glBufferSubData - 无法让它工作【英文标题】:OpenGL glBufferSubData - can't get it to work 【发布时间】:2019-09-16 11:05:02 【问题描述】:

我需要用新的顶点更新我的网格。我这样创建 VBO(最初创建时只有一个顶点):

public Mesh(float[] vertex, int size)

    texture = null;
    meshType = 1;           //will draw lines

    FloatBuffer verticesBuffer = null;
    IntBuffer indicesBuffer = null;
    int vboID;

    try
    
        vertexCount = size;

        vaoID = glGenVertexArrays();
        glBindVertexArray(vaoID);

        vboIDList = new ArrayList<>();

        // Vertices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        verticesBuffer = MemoryUtil.memAllocFloat(size * 3);        // !!! Must Be manually freed!
        verticesBuffer.put(vertex).flip();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        vertexAttrArrCount += 1;

        // Indices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        indicesBuffer = MemoryUtil.memAllocInt(size);             // !!! Must be manually freed!
        indicesBuffer.put(new int[]0).flip();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);

        // unbinding
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    
    finally
    
        if (verticesBuffer != null)
        
            MemoryUtil.memFree(verticesBuffer);                             // Freeing vertex buffer
        

        if (indicesBuffer != null)
        
            MemoryUtil.memFree(indicesBuffer);                              // Freeing indices buffer
        
    


然后我想更新 VBO 缓冲区并将新顶点写入其中。请注意,我确实创建了 VBO 以便为我的新顶点提供足够的空间,并且我确实控制它不会被过度填充。我还控制每次渲染调用绘制的元素数量,因此我不会绘制空的 0/0/0 顶点。

我的问题是,这段代码有效:

public void updateVBO(float[] vertices, int[] indices, int size)

    if (meshType == 1)
    
        lineCount = size;

        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        try
        
            //System.out.printf("Adding vertex (%f, %f, %f) to position %d\n",vertex.x,vertex.y,vertex.z,position);
            vboID = vboIDList.get(0);
            //float[] vert = new float[]vertex.x, vertex.y, vertex.z;
            subDataF = MemoryUtil.memAllocFloat(vertices.length);        // !!! Must Be manually freed!
            subDataF.put(vertices).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferData(GL_ARRAY_BUFFER, subDataF, GL_STATIC_DRAW);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            //int[] index = new int[] position ;
            subDataI = MemoryUtil.memAllocInt(indices.length);        // !!! Must Be manually freed!
            subDataI.put(indices).flip();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, subDataI, GL_STATIC_DRAW);

            //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        
        finally
        
            if (subDataF != null)
            
                MemoryUtil.memFree(subDataF);
            
            if (subDataI != null)
            
                MemoryUtil.memFree(subDataI);
            
        
    

所以当我传递整个顶点数组并从头开始重新分配 VBO 内存时,它会准确地绘制我需要的内容。 但是我想使用glBufferSubData,这样我每次添加新顶点时都不会重新分配内存。 而且这段代码不起作用:

public void addVertex(Vector3f vertex, int position)

    if (meshType == 1)
    
        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        lineCount = position+1;

        try
        
            System.out.printf("Adding vertex (%f, %f, %f) to position %d\n",vertex.x,vertex.y,vertex.z,position);
            vboID = vboIDList.get(0);
            float[] vert = new float[]vertex.x, vertex.y, vertex.z;
            subDataF = MemoryUtil.memAllocFloat(3);        // !!! Must Be manually freed!
            subDataF.put(vert).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ARRAY_BUFFER, position * 3 * 4, subDataF);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            int[] index = new int[] position ;
            subDataI = MemoryUtil.memAllocInt(1);        // !!! Must Be manually freed!
            subDataI.put(index).flip();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, position * 4, subDataI);

            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        
        finally
        
            if (subDataF != null)
            
                MemoryUtil.memFree(subDataF);
            
            if (subDataI != null)
            
                MemoryUtil.memFree(subDataI);
            
        
    

另外我知道它并没有优化我创建 floatbuffer 和 intbuffer 的方式,我只是想让它在我清理它之前工作。我尝试了很多东西,所以最后一段代码很奇怪。

不过,我不明白我做错了什么。我确实检查了我是否正确传递了所有数据,并且位置(和偏移量)似乎是按照它们应该的方式计算的。它只是不绘制任何东西,而当我使用 glBufferData 时它会绘制。

谁能解释我在哪里犯了错误?

在所有建议之后,这是我最终的结果,但它仍然根本不起作用:

public void addVertex(Vector3f vertex, int position)

    if (meshType == 1)
    
        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        lineCount = position+1;

        try
        
            System.out.printf("Adding vertex (%f, %f, %f) to position %d\n",vertex.x,vertex.y,vertex.z,position);
            vboID = vboIDList.get(0);
            float[] vert = new float[]vertex.x, vertex.y, vertex.z;
            subDataF = MemoryUtil.memAllocFloat(3);        // !!! Must Be manually freed!
            subDataF.put(vert).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ARRAY_BUFFER, (long)(position * 3 * 4), (FloatBuffer)subDataF);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            int[] index = new int[] position ;
            subDataI = MemoryUtil.memAllocInt(1);        // !!! Must Be manually freed!
            subDataI.put(index).flip();

            glBindVertexArray(vaoID);
            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, (long)(position * 4), (IntBuffer)subDataI);
        
        finally
        
            if (subDataF != null)
            
                MemoryUtil.memFree(subDataF);
            
            if (subDataI != null)
            
                MemoryUtil.memFree(subDataI);
            
        
    

我确实检查了 VAO ID 是否正确。

【问题讨论】:

参数position是什么?为什么它用于顶点数组和索引数组?当您只想更改单个顶点坐标时,为什么要更改索引缓冲区? 位置基本上是当前在网格中创建的顶点数。它用于顶点 VBO 以提供偏移量 - 如果位置为 0,我们从 VBO 中的第 0 个字节开始写入,如果它是一个,我们写入第 12 个字节,即第 2 个顶点,依此类推。与索引数组相同 - 对于每个顶点创建一个索引。碰巧现在就我的目的而言,索引是相等的位置,所以索引数组看起来像 0, 1, 2, 3, ....。这是因为现在我基本上想画线,第一行从 0 到 1,第二行从 1 到 2 等等 - 这就是它们被排序的原因。 【参考方案1】:

正如我所想的那样,这是一些愚蠢的事情,与 VAO 绑定等完全没有关系。

问题是,当我最初创建 VBO 时,我会这样做:

// Vertices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        verticesBuffer = MemoryUtil.memAllocFloat(size * 3);        // !!! Must Be manually freed!
        verticesBuffer.put(vertex).flip();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        vertexAttrArrCount += 1;

我假设因为我为 size*3 浮点数分配缓冲区,所以它将是那个大小,当我把它放入 VBO 时 - 它会分配 size*3*4 字节,即足够 size*3 浮点数。

结果不!因为我只将一个顶点(3 个浮点数)放入缓冲区 - 它只会分配该数量的空间。因此,当我后来尝试使用 glBufferSubData 时 - 它在 GPU 上只有 3 个浮点数的空间,自然不会将值放在我需要的地方。我真的很惊讶它并没有完全崩溃。

为了解决这个问题,我现在这样做了:

// Vertices VBO generation
        ...
        verticesBuffer.put(vertex).put(new float[size*3 - 3]).flip();
        ...

所以基本上我是手动将一个空数组放入 FloatBuffer 中,这样可以确保缓冲区大小合适。

结果如下: 构造函数:

public Mesh(float[] vertex, int size)

    texture = null;
    meshType = 1;           //will draw lines

    FloatBuffer verticesBuffer = null;
    IntBuffer indicesBuffer = null;
    int vboID;

    try
    
        vertexCount = size;

        vaoID = glGenVertexArrays();
        glBindVertexArray(vaoID);

        vboIDList = new ArrayList<>();

        // Vertices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        verticesBuffer = MemoryUtil.memAllocFloat(size * 3);        // !!! Must Be manually freed!
        verticesBuffer.put(vertex).put(new float[size*3 - 3]).flip();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        vertexAttrArrCount += 1;

        // Indices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        indicesBuffer = MemoryUtil.memAllocInt(size);             // !!! Must be manually freed!
        indicesBuffer.put(new int[size]).flip();                  // I need the first element 0 anyway
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);

        // unbinding
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    
    finally
    
        if (verticesBuffer != null)
        
            MemoryUtil.memFree(verticesBuffer);                             // Freeing vertex buffer
        

        if (indicesBuffer != null)
        
            MemoryUtil.memFree(indicesBuffer);                              // Freeing indices buffer
        
    


然后更新:

public void addVertex(Vector3f vertex, int position)

    if (meshType == 1)
    
        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        lineCount = position+1;

        try
        
            vboID = vboIDList.get(0);
            float[] vert = new float[]vertex.x, vertex.y, vertex.z;
            subDataF = MemoryUtil.memAllocFloat(vert.length);        // !!! Must Be manually freed!
            subDataF.put(vert).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ARRAY_BUFFER, (long)(position * 3 * 4), (FloatBuffer)subDataF);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            int[] index = new int[] position ;
            subDataI = MemoryUtil.memAllocInt(index.length);        // !!! Must Be manually freed!
            subDataI.put(index).flip();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, (long)(position * 4), (IntBuffer)subDataI);

            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        
        finally
        
            if (subDataF != null)
            
                MemoryUtil.memFree(subDataF);
            
            if (subDataI != null)
            
                MemoryUtil.memFree(subDataI);
            
        
    

而且它有效。请注意,代码有点脏,我在发布答案之前没有清理它。

【讨论】:

我假设绑定 VAO (glBindVertexArray(vaoID);) 后它是持久的。这意味着这个状态永远不会改变。这将解释 GL_ELEMENT_ARRAY_BUFFER 行为。没有必要绑定VAO,因为它仍然绑定了(所以元素缓冲区。但是不要固执,相信我,必须绑定VAO才能更改GL_ELEMENT_ARRAY_BUFFER的数据!如果您有超过 1 个 VAO 对象,这将导致问题。永远不要这样做glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 您不需要分配一个数组并将其放入VBO。您可以将null 而不是顶点数组传递给您的glBufferData,它将根据您的size 分配缓冲区。 Rabbid76,如果你没有注意到,VAO 在构造函数 glBindVertexArray(0) 的末尾被解除绑定;问题是,在 OGL API 中,VAO 和 VBO 的 presist 都不会去任何地方。某些 VBO 与某些 VAO 相关联。渲染时需要它们,但不需要绑定 VAO 来操作与其关联的 VBO。您可以直接使用 VBO。 _____ 收割者,但我需要已经有第一个顶点。我想知道什么更快 - 将它绑定到 null 然后立即调用 glBufferSubData(),或者像我一样填充一个数组并只调用一次 glBufferData。 >>>> 如果您有超过 1 个 VAO 对象,这将导致问题。永远不要做 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);..... Ermmm...,我的场景中有 20 个网格,每个网格都有自己的 VAO 和不同数量的 VBO。我也对它们使用不同的着色器程序,并根据对象传递不同的数据。到目前为止,我没有注意到任何问题——即使我按照我应该的方式解除了 VBO 的绑定。你显然错过了一些观点 - 或者我可能会这样做。但到目前为止,我没有发现任何问题,一切正常。 @VladKozmyuk 阅读规范。我提到这一点是因为我想帮助你,而且我已经有几十年的经验了。不要混淆GL_ELEMENT_ARRAY_BUFFERGL_ARRAY_BUFFER。这个缓冲区目标的行为完全不同。您实际上所做的是将元素缓冲区绑定到默认顶点数组对象 0。这仅适用于兼容性配置文件,但会导致 核心配置文件中的错误上下文!!!! GL_ELEMENT_ARRAY_BUFFER 在 VAO 中声明,与 GL_ARRAY_BUFFER 相比,GL_ARRAY_BUFFER 是一个全局状态。

以上是关于OpenGL glBufferSubData - 无法让它工作的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL glBufferSubData - 无法让它工作

为啥 glBufferSubData 需要等到 glDrawElements 不使用 VBO?

glBufferData 和 glBufferSubData 偏移量

结构的 glBufferSubData 偏移量

OpenGL +无法使用buffersubdata更新缓冲区数据

glBufferSubData 啥时候返回? [复制]