绑定到OpenGL 3.x VBO

时间:2011-09-01 21:45:15

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

我正在尝试更新我曾经使用OpenGL 2.x样式顶点数组的引擎来使用OpenGL 3.x,这意味着更新到VAO / VBO。我想我并没有正确地约束VBO。请阅读下面的更多信息或跳到代码并找到我做错了。

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

  • root MeshNode

MeshNode

  • 变换
  • VAO指数
  • 索引VBO索引
  • 子MeshNodes数组
  • MeshObjects数组

MeshObject

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

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

我已经通过gDEbugger检查了Visual Studio调试器中的顶点数据和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);
}

2 个答案:

答案 0 :(得分:5)

  

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

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

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

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

答案 1 :(得分:1)

我认为你错误地使用了glDrawRangeElementsstartend参数是索引数组中可能出现的最小和最大顶点索引,但是您提供了m_indexOffestm_indexOffset+m_indices.size(),这是索引数据你渲染的范围,不一定是那些索引数组中顶点索引的范围。

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