简体   繁体   中英

OpenGL - Drawing Large Amounts of Information stored in VBO's

I have fairly large C++ objects which load mesh data into memory and then draws based on an OnDisplay callback.

The problem is that the refresh rate is really slow which I suspect is because my code is poorly written.

Anyway; here is what my class looks like (function prototypes shown to give you an idea of how my class is set up).

What I want to know is if it is possible to just call the "glDrawElements" function somehow on what is in memory if most of my VBOs haven't changed and skip my Begin and end draw functions as shown below.

OR, even better,

If there is a magic OpenGL function I can call that, with one pass, OpenGL can render all of my unchanged Buffer IDs and I can simply focus on drawing the ones that have changed and the camera?

Mostly I will just have the camera moving through the scene.

I set these functions up based on tutorials and documentation so I know they work; I just want to speed up the drawing, especially when the meshes I am loading in are 100MB + in size.

First, here is my class prototype:

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.
};

And here is my Initialize Function:

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

Here is my BeginDraw Function:

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

My Draw function ...

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

And finally my End Draw Function....

void VBOMesh::EndDraw() const
{
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);    
} 

What I want to know is if it is possible to just call the "glDrawElements" function somehow on what is in memory if most of my VBOs haven't changed and skip my Begin and end draw functions as shown below.

There is a feature in opengl that exactly does that, called Vertex Array Buffer (VAO) . This feature allows you to save what you have in your begin draw into an object ( much like a VBO ) bind it, unbind it, saving you time so you don't have to bind all buffers by hand each time. I don't really remember since when it is supported, it is a core feature since opengl3 I'm sure about that, as far as I know even OpenGL ES 2.0 supports this via extension .

If there is a magic OpenGL function I can call that, with one pass, OpenGL can render all of my unchanged Buffer IDs and I can simply focus on drawing the ones that have changed and the camera?

If I understand this correctly, you want something like a cached rendering so instead of manually calling glDrawElements each time you want a function where you can throw in all your buffer id's and tell it to 'render these'. As far as I know the closest thing to this is instanced rendering , but that comes with it's limitations.

Altho I think there might be something else here since VBOs already make your rendering fast, and GPUs don't like small models, large models are really good for the GPU since it gets a chance to use its nifty features, super duper caching and what not to make it fast, where with small models there is not chance since before caches start to fill up the model is already rendered. So if this runs slow in your case it might be something else since what you are doing is almost ideal for a GPU to reach its top performance, I would suggest running something like gDebugger to profile which is the function or code piece that takes the most time and if that seems okay, then try a GPU debugger/profiler to see what takes the most time ( like NVPerfKit for nVidia ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM