OpenGL - 绘制存储在 VBO 中的大量信息

Posted

技术标签:

【中文标题】OpenGL - 绘制存储在 VBO 中的大量信息【英文标题】:OpenGL - Drawing Large Amounts of Information stored in VBO's 【发布时间】:2014-05-04 06:27:34 【问题描述】:

我有相当大的 C++ 对象,它们将网格数据加载到内存中,然后根据 OnDisplay 回调进行绘制。

问题是刷新率真的很慢,我怀疑是因为我的代码写得不好。

无论如何;这是我的类的样子(显示的函数原型让您了解我的类是如何设置的)。

我想知道的是,如果我的大多数 VBO 没有改变并跳过我的开始和结束绘制函数,是否可以在内存中以某种方式调用“glDrawElements”函数,如下所示.

或者,甚至更好,

如果有一个神奇的 OpenGL 函数,我可以调用它,通过一次,OpenGL 可以渲染我所有未更改的缓冲区 ID,我可以专注于绘制已更改的缓冲区 ID 和相机?

大多数情况下,我只会让相机在场景中移动。

我根据教程和文档设置了这些功能,因此我知道它们可以工作;我只是想加快绘图速度,尤其是当我加载的网格大小为 100MB 以上时。

首先,这是我的类原型:

class MyMeshData

public:
    MyMeshData();
    ~MyMeshData();

    // Save up data into GPU buffers.
    bool Initialize(const MeshDataFromFileClass * StaticMeshData);

    // Update vertex positions for deformed meshes.
    void UpdateVertexPosition(const MeshDataFromFileClass * StaticMeshData, const MyVector4Class * pVertices) const;

    // Bind buffers, set vertex arrays, turn on lighting and texture.

    void BeginDraw(ShadingMode pShadingMode) const;

    // Draw all the faces with specific material with given shading mode.

    void Draw(int pMaterialIndex, ShadingMode pShadingMode) const;

    // Unbind buffers, reset vertex arrays, turn off lighting and texture.
    void EndDraw() const;

    // Get the count of material groups
    int GetSubMeshCount() const  return mSubMeshes.GetCount(); 

private:
    enum
    
        VERTEX_VBO,
        NORMAL_VBO,
        UV_VBO,
        INDEX_VBO,
        VBO_COUNT,
    ;

    // For every material, record the offsets in every VBO and triangle counts
    struct SubMesh
    
        SubMesh() : IndexOffset(0), TriangleCount(0) 

        int IndexOffset;
        int TriangleCount;
    ;

    GLuint mVBONames[VBO_COUNT];

    MyMeshArray<SubMesh*> mSubMeshes;
    bool mHasNormal;
    bool mHasUV;
    bool mAllByControlPoint; // Save data in VBO by control point or by polygon vertex.
;

这是我的初始化函数:

bool Initialize(const MeshDataFromFileClass * StaticMeshData) 
    [...]
    /*
    Earlier code that retrieves data from file removed.

    Only the point where the data is transferred to the GPU is shown.
    */

        // Create VBOs
    glGenBuffers(VBO_COUNT, mVBONames);

    // Save vertex attributes into GPU
    glBindBuffer(GL_ARRAY_BUFFER, mVBONames[VERTEX_VBO]);
    glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * VERTEX_STRIDE * sizeof(float), lVertices, GL_STATIC_DRAW);
    delete [] lVertices;

    if (mHasNormal)
    
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[NORMAL_VBO]);
        glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * NORMAL_STRIDE * sizeof(float), lNormals, GL_STATIC_DRAW);
        delete [] lNormals;
    

    if (mHasUV)
    
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[UV_VBO]);
        glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * UV_STRIDE * sizeof(float), lUVs, GL_STATIC_DRAW);
        delete [] lUVs;
    

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBONames[INDEX_VBO]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, lPolygonCount * TRIANGLE_VERTEX_COUNT * sizeof(unsigned int), lIndices, GL_STATIC_DRAW);

    delete [] lIndices;

这是我的 BeginDraw 函数:

void MyMeshData::BeginDraw(ShadingMode pShadingMode) const


    glBindBuffer(GL_ARRAY_BUFFER, mVBONames[VERTEX_VBO]);
    /*
    glVertexPointer(VERTEX_STRIDE, GL_FLOAT, 0, 0);
    glEnableClientState(GL_VERTEX_ARRAY);
    */
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, VERTEX_STRIDE, GL_FLOAT, GL_FALSE, 0, 0);



    // Set normal array.
    if (mHasNormal && pShadingMode == SHADING_MODE_SHADED)
    

        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[NORMAL_VBO]);
        glNormalPointer(GL_FLOAT, 0, 0);
        glEnableClientState(GL_NORMAL_ARRAY);

    

    // Set UV array.
    if (mHasUV && pShadingMode == SHADING_MODE_SHADED)
    
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[UV_VBO]);
        glTexCoordPointer(UV_STRIDE, GL_FLOAT, 0, 0);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);        
    


    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBONames[INDEX_VBO]);

    if (pShadingMode != SHADING_MODE_SHADED)
    
        glColor4fv(DEFAULT_WIREFRAME_COLOR);        
    

我的绘图功能 ...

void MyMeshData::Draw(int pMaterialIndex, ShadingMode pShadingMode) const

    // Where to start.
    GLsizei lOffset = mSubMeshes[pMaterialIndex]->IndexOffset * sizeof(unsigned int);
    if ( pShadingMode == SHADING_MODE_SHADED)
    
        const GLsizei lElementCount = mSubMeshes[pMaterialIndex]->TriangleCount * 3;
        glDrawElements(GL_TRIANGLES, lElementCount, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(lOffset));
    
    else
    
        for (int lIndex = 0; lIndex < mSubMeshes[pMaterialIndex]->TriangleCount; ++lIndex)
        
            glDrawElements(GL_LINE_LOOP, TRIANGLE_VERTEX_COUNT, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(lOffset));
            lOffset += sizeof(unsigned int) * TRIANGLE_VERTEX_COUNT;            
        
    

最后是我的结束绘制函数......

void VBOMesh::EndDraw() const

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);    
 

【问题讨论】:

您的代码几乎无法阅读。在某个地方, 被 html &amp;lt;&amp;gt; 取代。我建议重新粘贴它,因为您的模板代码现在是乱码。 @AndonM.Coleman 谢谢;我什至没有注意到这一点。进行了修改。 【参考方案1】:

我想知道是否可以只调用 如果我的大部分时间,“glDrawElements”以某种方式对内存中的内容起作用 VBO 没有改变并跳过我的 Begin 和 end draw 函数,如图所示 下面。

opengl 中有一个功能可以做到这一点,称为Vertex Array Buffer (VAO)。此功能允许您将开始绘制中的内容保存到对象中(很像 VBO)绑定它,取消绑定它,节省您的时间,因此您不必每次都手动绑定所有缓冲区。我真的不记得什么时候支持它了,它是自 opengl3 以来的核心功能,我对此很确定,据我所知,甚至 OpenGL ES 2.0 也通过extension 支持它。

如果有一个神奇的 OpenGL 函数,我可以一次性调用它, OpenGL 可以渲染我所有未更改的缓冲区 ID,我可以简单地 专注于画出变化的和相机?

如果我理解正确的话,您需要像缓存渲染这样的东西,而不是每次您想要一个函数时手动调用 glDrawElements,您可以在其中输入所有缓冲区 id 并告诉它“渲染这些”。据我所知,与此最接近的是instanced rendering,但这也有其局限性。

尽管如此,我认为这里可能还有其他东西,因为 VBO 已经使您的渲染速度更快,而且 GPU 不喜欢小型模型,大型模型对 GPU 非常有用,因为它有机会使用其漂亮的功能,超级骗子缓存以及如何不使其快速,对于小型模型,没有机会,因为在缓存开始填满之前,模型已经被渲染。因此,如果这在您的情况下运行缓慢,则可能是其他原因,因为您正在做的事情几乎是 GPU 达到其最佳性能的理想选择,我建议运行类似 gDebugger 的东西来分析哪个函数或代码片段需要最多的时间,如果看起来没问题,然后尝试使用 GPU 调试器/分析器来查看花费最多的时间(例如 NVPerfKit 用于 nVidia)。

【讨论】:

ES 2.0 不支持 VAO。 ES 3.0 可以。 谢谢我已经编辑了,我忘了它只支持通过es 2.0中的扩展。 @DrakkLord:你确定你的意思是 instanced 渲染吗?我认为indirect rendering 在这里会更相关,尽管这两个概念并不真正适合问题中提出的数据结构。 nvidia 声称使用 VAO 比不使用它要慢一些(遗憾的是,这是真的,尽管差异很小)。另一方面,在 ES 上,VAO 通常要快得多。但无论如何,对顶点、法线和纹理坐标使用单独的 VBO 是不好的——这一切都应该合并到一个交错的 VBO 中。 @DrakkLord 谢谢我会试试这个。

以上是关于OpenGL - 绘制存储在 VBO 中的大量信息的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL中的动态VBO

C++/OpenGL - 绘制立方体 VBO

将 OpenGL 绘制列表转换为顶点数组或 VBO

iOS OpenGL ES 2.0 VBO 混淆

OpenGL不绘制三角形VBO

切换到使用交错 VBO - 编译但没有绘制 - opengl es 2.0