【问题标题】:Binding to OpenGL 3.x VBO绑定到 OpenGL 3.x VBO
【发布时间】:2011-11-08 18:26:47
【问题描述】:

我正在尝试更新我曾经使用 OpenGL 2.x 样式顶点数组的引擎以与 OpenGL 3.x 一起使用,这意味着更新到 VAO/VBO。我认为我没有正确绑定 VBO。阅读下文了解更多信息或跳至代码并找出我做错了什么。

我的网格类的快速概览如下所示:

网格

  • 根网格节点

网格节点

  • 变换
  • VAO 指数
  • 索引 VBO 索引
  • 子网格节点数组
  • MeshObject 数组

网格对象

  • 从文件加载的所有顶点和索引数据,用于单个总网格
  • 顶点 VBO 索引

如果我只用一个 MeshObject 绘制一个 MeshNode,它似乎画得很好。当我用多个 MeshObject 绘制一个 MeshNode 时,我得到的东西是我试图绘制的模型的一般形状,但有点乱码。

我已经在 Visual Studio 调试器中检查了顶点数据,并通过 gDEbugger 检查了 VBO 数据,一切看起来都很好,所以我很确定从文件加载并加载到 VBO 中工作正常。

我使用 gDEbugger 强制它为所有顶点而不是三角形绘制点,并且它具有单个 MeshObject 的形状,这让我相信我只是没有正确绑定到不同的 VBO。好像它在尝试使用不同的索引进行绘制,但每次都使用相同的顶点。

VertexData 看起来像这样:

struct VertexData
{
    enum
    {
        NUM_TEXCOORDS = 1,
    };
    vector3 vertex;
    vector3 normal;
    vector2 texCoord[NUM_TEXCOORDS];
};

相关MeshNode代码:

void MeshNode::initVAO(void)
{
    closeVAO();

    unsigned int scan;

    //init index data
    if (m_meshObjects.size() > 0)
    {
        glGenVertexArrays(1, &m_meshVAO);
        glBindVertexArray(m_meshVAO);
        {
            //add up the total index count for all the mesh objects in this node
            unsigned int indexCount = 0;
            for (scan = 0; scan < m_meshObjects.size(); ++scan)
            {
                indexCount = indexCount + m_meshObjects[scan].getIndices()->size();
            }
            //make the actual index buffer
            glGenBuffers(1, &m_indexVBO);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexVBO);
            {
                glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * sizeof(unsigned short), NULL, GL_STATIC_DRAW);

                //set up VBOs and fill the index buffer with the index data from each mesh object
                unsigned int offset = 0;
                for (scan = 0; scan < m_meshObjects.size(); ++scan)
                {
                    m_meshObjects[scan].initVBOs(offset);
                }
            }
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
        glBindVertexArray(0);
    }
    for (scan = 0; scan < m_childMeshNodes.size(); ++scan)
    {
        m_childMeshNodes[scan]->initVAO();
    }
}

void MeshNode::closeVAO(void)
{
    if (m_meshVAO != 0)
    {
        glBindVertexArray(m_meshVAO);
        {
            glDeleteBuffers(1, &m_indexVBO);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
        glBindVertexArray(0);
        glDeleteVertexArrays(1, &m_meshVAO);
        m_meshVAO = 0;
        m_indexVBO = 0;
    }
}

void MeshNode::render(const matrix4 &_parentTransform)
{
    matrix4 transform = _parentTransform * m_transform;

    if (m_meshObjects.size() > 0)
    {
        glBindVertexArray(m_meshVAO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexVBO);
        {
            for (unsigned int objectScan = 0; objectScan < m_meshObjects.size(); ++objectScan)
            {
                m_meshObjects[objectScan].render(transform);
            }
        }
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }

    for (unsigned int childScan = 0; childScan < m_childMeshNodes.size(); ++childScan)
    {
        m_childMeshNodes[childScan]->render(transform);
    }
}

相关MeshObject代码:

void MeshObject::initVBOs(unsigned int& _indexOffset)
{
    //sub in this section of the index data
    m_indexOffset = _indexOffset;
    _indexOffset = _indexOffset + m_indices.size();
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, m_indexOffset * sizeof(unsigned short), m_indices.size() * sizeof(unsigned short), &(m_indices[0]));

    //init vertex data
    glGenBuffers(1, &m_vertexVBO);
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO);
    {
        glBufferData(GL_ARRAY_BUFFER, m_data.size() * sizeof(VertexData), &(m_data[0]), GL_STATIC_DRAW);

        glVertexAttribPointer(Shader::POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (char*)0);
        glEnableVertexAttribArray(Shader::POSITION);
        glVertexAttribPointer(Shader::NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (char*)12);
        glEnableVertexAttribArray(Shader::NORMAL);
        glVertexAttribPointer(Shader::TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (char*)24);
        glEnableVertexAttribArray(Shader::TEXCOORD0);
    }
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void MeshObject::closeVBOs(void)
{
    glDeleteBuffers(1, &m_vertexVBO);

    m_vertexVBO = 0;
}

void MeshObject::render(const matrix4& _transform)
{
    m_material->bind(_transform);
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexVBO);
    {
        glEnableVertexAttribArray(Shader::POSITION);
        glEnableVertexAttribArray(Shader::NORMAL);
        glEnableVertexAttribArray(Shader::TEXCOORD0);
        glDrawRangeElements(GL_TRIANGLES, m_indexOffset, m_indexOffset + m_indices.size(), m_indices.size(), GL_UNSIGNED_SHORT, (char*)0);
        glDisableVertexAttribArray(Shader::POSITION);
        glDisableVertexAttribArray(Shader::NORMAL);
        glDisableVertexAttribArray(Shader::TEXCOORD0);
    }
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

【问题讨论】:

  • +1:详细说明。它使找到解决方案变得更加容易。

标签: c++ opengl 3d vbo opengl-3


【解决方案1】:

我的网格类的快速概览如下所示:

我认为您的场景图层次结构有点混乱。场景图中的所有Node 都需要一个变换、Node 子项列表以及要在该Node 中绘制的网格列表。它不应该有元素缓冲区或 VAO;这些在概念上是网格数据的一部分。

确实,您的问题源于最后一点。 Vertex Array Objects 包含由 glVertexAttrib(I)Pointer 调用设置的状态。这意味着,每次您在同一个 MeshNode 中调用 MeshObject::initVBOs 时,都会覆盖上一次调用所设置的数据。

每个 mesh 都需要自己的 VAO,而不是节点。网格可以共享索引数据,这就是您似乎正在做的事情(尽管如果您担心缓冲区过多,它们也应该为其顶点数据共享相同的缓冲区对象)。但 VAO 需要有所不同。多个 VAO 可以引用同一个元素缓冲区。

【讨论】:

  • 好地方,完全忽略了这一点。我实际上搜索了这样一个错误,但找不到它。
  • @Nicol 在这个答案,尤其是您对 Christian 的答案的评论之间,VAO 比花 3 天时间在网上翻阅 OpenGL 文档和各种教程更有意义。希望我早先看到某个地方没有将这些信息埋在 10 页的绒毛中。非常感谢。
  • @Apostate:我所说的一切都包含在我链接到的 VAO 文章中。 OpenGL Wiki 是一个相当不错的信息来源。此外,大多数教程都是按顺序阅读的;它们并不是关于特定主题的真正可搜索的信息缓存。
  • @Nicol:现在我明白了,回头看,确实一切都在那里。当他们真正需要说的是:VAO 的句柄更改状态,您根据当前绑定的 VBO 修改 VAO,它们处理 x、y 和 z 功能时,他们只花了几页来描述它,就好像它是一个结构一样。在他们向您保证他们非常混乱之后,重要的部分都被埋在了底部。并且没有示例代码。我想我只是更喜欢简短而甜蜜。
【解决方案2】:

我认为您对glDrawRangeElements 的使用不正确。 startend 参数是索引数组中可能出现的最小和最大顶点索引,但是您提供然后 m_indexOffestm_indexOffset+m_indices.size(),这是您呈现的索引数据范围,不一定是范围这些索引数组中的顶点索引。

附带说明,VAO 封装了每个顶点数组/缓冲区状态、所有缓冲区绑定、指针和启用标志,因此您进行了许多不必要的调用。只需在initVAO 方法中绑定索引缓冲区,然后当您在MeshNode::render 方法中绑定VAO 时它总是被绑定(当然在这种情况下省略所有glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) 调用)。同样,您是否只需要在initVAO 方法中启用属性数组(假设所有MeshObjects 都启用了相同的属性)并且当您绑定VAO 时它们会自动启用。在您当前的配置中,VAO 是完全不必要的,您不会以任何方式从中获利。

【讨论】:

  • VAO 不封装“所有缓冲区绑定”。它封装的唯一缓冲区绑定是GL_ELEMENT_ARRAY_BUFFER;甚至GL_ARRAY_BUFFER 也没有直接封装。只有 glVertexAttrib(I)PointerglEnable/DisableVertexAttribArrayglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *) 调用会影响绑定的 VAO。
猜你喜欢
  • 1970-01-01
  • 2011-12-23
  • 1970-01-01
  • 1970-01-01
  • 2021-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多