Optimization of OpenGL rendering

Viewed 270

Currently my scene consists of models. Models consist of meshes. Each mesh has its own buffers, there are 8 of them (vertices, normals, texCoords, tangents, bitangents, boneWeights). I render each frame like this:

void drawModel(Model &model) {
    ...
    for (size_t i = 0; i < model.shape->meshes.size(); i++) {
        ...

        pointer(cs.inPosition, 3, model.shape->meshes[i].getVerticesBuffer());
        pointer(cs.inNormal, 3, model.shape->meshes[i].getNormalsBuffer());
        pointer(cs.inTangent, 3, model.shape->meshes[i].getTangentsBuffer());
        pointer(cs.inBitangent, 3, model.shape->meshes[i].getBitangentsBuffer());
        pointer(cs.inTexCoord, 2, model.shape->meshes[i].getTexCoordsBuffer());
        ...
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.shape->meshes[i].getIndicesBuffer());
        glDrawElements(GL_TRIANGLES, model.shape->meshes[i].indices.size(), GL_UNSIGNED_INT, nullptr);
    }
}

pointer() code:

inline void pointer(GLint location, int count, GLuint buffer) {
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glVertexAttribPointer(
        location, // attribute location
        count,    // count (1, 2, 3 or 4)
        GL_FLOAT, // type
        GL_FALSE, // is normalized?
        0,        // step
        nullptr   // offset
    );
}

I also use only one VAO for the whole scene.

How can I optimize this code (Maybe I should create these buffers only for the model, not for each mesh? Maybe it is worth allocating a separate VAO for each model?)? My scene consists of a fairly large number of vertices (about 500k), and shows a fairly low FPS (about 35).

1 Answers

Create your VAOs during startup & initialization, not during your render loop. As @Ripi2 said, changing it causes a context roll.

The VAO will maintain state including which buffers are referenced (but not the buffer's data as that's still stored in the VBO) and all of the vertex attrib data. The whole point of using a VAO is to avoid having to reset that stuff all the time. Create one per vertex buffer (or one per model), store it in your Model or Shape class. When rendering, just bind & unbind that before glDraw*.

Be mindful of byte alignment in your buffers. It's preferable to not have individual values crossing alignment boundaries as that will increase fetch times in your shader, and it's not all that cache-friendly. Using an interlaced buffer rather than a separate buffer for each vertex component may make this easier without sacrificing too much space in the buffer. However, using separate buffers for each attribute is a common practice - for instance, Unity uses this approach - so this may not help.

For complex scenes, you'll also want to implement frustum culling at the very least. Occlusion culling and other semi-advanced features will also help reduce your render time. Sorting your models front-to-back before drawing can will reduce overdraw since the fragment shader will not be called for occluded fragments.

If you have a lot of small models that share materials, considering drawing them in a single draw call. If you have a lot of identical models, consider using instanced drawing.

Also, don't be shy to add some timing data and use your favorite profiling tool to identify performance bottlenecks.

Related