java - OpenGL glBufferSubData - 无法让它工作

标签 java lwjgl vbo opengl-es-3.0

我需要用新顶点更新我的网格。我这样创建了 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 是否正确。

最佳答案

正如我所想,这是愚蠢的事情,与 VAO 绑定(bind)等完全无关。

问题是,当我最初创建 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 float 分配了缓冲区,所以它将具有该大小,并且当我将其放入 VBO 中时 - 它将分配 size*3*4 字节,即足以容纳 size*3 float 。

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

为了解决这个问题,目前我这样做了:

// 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);
            }
        }
    }
}

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

关于java - OpenGL glBufferSubData - 无法让它工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57955500/

相关文章:

c++ - OpenGL 线条未着色

java - ThreadPoolExecutor 的 corePoolSize=0 如何工作?

java - LibGDX 如何使用 Sprites 和 SpriteBatch 在单个位置组合 2 个纹理以进行多重纹理

java - 如何使用 lwjgl 将相机视线方向用作 z 方向

java - 使用轻量级 java 游戏库与 3d 图形进行更多斗争

c++ - OpenGL - 加载多面的正确方法

opengl - 在 glDrawElements (OpenGL/VBO) 中对顶点和法线使用不同的数组

java - Jenkins 中的 PermGen Space 问题

java - 整数和双差

java - Tomcat配置