8

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

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

  • 根网格节点

网格节点

  • 转换
  • VAO指数
  • 指数 VBO 指数
  • 子网格节点数组
  • 网格对象数组

网格对象

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

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

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

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

顶点数据看起来像这样:

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

2 回答 2

5

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

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

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

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

于 2011-09-01T21:58:34.180 回答
1

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

And as a side note, a VAO encapsulates every vertex array/buffer state, all buffer bindings, the pointers and the enabled flags, so you're making many unneccessary calls. Just bind the index buffer in the initVAO method and then it always gets bound when you bind the VAO in the MeshNode::render method (of course in this case omit all the glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) calls). Likewise do you only need to enable the attribtue arrays in the initVAO method (assuming all MeshObjects have the same attributes enabled) and they automatically get enabled when you bind the VAO. In your current configuration the VAO is completely unneccessary and you don't proft from it in any way.

于 2011-09-01T22:03:56.347 回答