简体   繁体   中英

Indices Problem with a Batch Renderer (OpenGL)

I'm trying to implement batch rendering for 3D objects in an engine I'm doing, and I can't manage to get the indices fine.

So in a 3D Renderer class I have a Renderer3DData structure that looks like the next:

    static const uint MaxQuads = 20000;
    static const uint MaxVertices = MaxQuads * 4;
    static const uint MaxIndices = MaxQuads * 6;

    uint IndicesDrawCount = 0; // Debug var
    std::vector<uint> Indices;
    Ref<IndexBuffer> IBuffer    = nullptr;
    // Other data like a VBuffer, VArray...

So the vector of Indices will store the indices to draw on each batch while the IBuffer is the Index Buffer class which handles all OpenGL operations ("Ref" is a typedef to make a shared pointer).

Then a static Renderer3DData* s_3DData; is initialized in the init function and the index buffer is initialized as follows:

    uint* indices = new uint[s_3DData->MaxIndices];
    s_3DData->IBuffer = IndexBuffer::Create(indices, s_3DData->MaxIndices);

And then bounded together with the Vertex Array and the Vertex Buffer, the initialization process is properly done since without batching this works.

So on each new batch the VArray gets bound and the Indices vector gets cleared and, on each mesh drawn, it gets modified like this:

    uint offset = 0;
    std::vector<uint> indices = mesh->m_Indices;
    for (uint i = 0; i < indices.size(); i += 6)
    {
            s_3DData->Indices.push_back(offset + 0 + indices[i]);
            s_3DData->Indices.push_back(offset + 1 + indices[i]);
            s_3DData->Indices.push_back(offset + 2 + indices[i]);

            s_3DData->Indices.push_back(offset + 3 + indices[i]);
            s_3DData->Indices.push_back(offset + 4 + indices[i]);
            s_3DData->Indices.push_back(offset + 5 + indices[i]);

            offset += 4;
            s_3DData->IndicesDrawCount += 6;
    }

I don't know how I did come up with this way of setting the index buffer, I was testing things to do it, pushing only the indices or the indices + offset doesn't works neither. Finally, on each draw, I do the next:

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, BufferID);
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s_3DData->Indices.size(), s_3DData->Indices.data());

    // With the vArray bound:
    glDrawElements(GL_TRIANGLES, s_3DData->IndicesDrawCount, GL_UNSIGNED_INT, nullptr);

As I mentioned, when I'm not batching, the drawing (which doesn't goes through all this process), works, so the data in the mesh and the vertex/index buffers must be good, what I think it's wrong is the way to set the index buffer since I'm not sure how to even set it up (unlike other rendering stuff).

The result is the next one (should be a solid sphere): 在此处输入图片说明

The way that "sphere" is rendered makes me think that the indices are wrong. And the objects in the center are objects drawn without batching for me to know that it's not the initial setup that's wrong. Does anybody sees what I'm doing wrong?

I finally solved it (I'm crying, I've been with this a lot of time).

So there was a couple of problems:

First: The function s_3DData->IBuffer = IndexBuffer::Create(indices, s_3DData->MaxIndices); that I posted was doing the next:

glCreateBuffers(1, &m_BufferID);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferID);
glBufferData(GL_ARRAY_BUFFER, count * sizeof(uint), nullptr, GL_STATIC_DRAW);

So the first problem was that I was creating index buffers with GL_STATIC_DRAW instead of GL_DYNAMIC_DRAW as required to batch since we are dynamically updating the buffer (this was my bad to not to post the function entirely, I was pretty asleep when I posted it, I should have done it).

Second: The function glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s_3DData->Indices.size(), s_3DData->Indices.data()); was wrong on the size parameter.

OpenGL requires the size of this function to be the total size of the buffer that we want to update, which is not the vector size but the vector size multiplied by sizeof(uint) (in this case, uint because the vector is a uint vector).

Third: And final problem was the loop that modified the indices vector on each mesh draw, it was wrong and thought from the point of view of drawing quads in 2D (as I was previously testing batching in 2D).

The correct loop is the next:

std::vector<uint> indices = mesh->m_Indices;
for (uint i = 0; i < indices.size(); ++i)
{
    s_3DData->Indices.push_back(s_3DData->IndicesCurrentOffset + indices[i]);               
    ++s_3DData->IndicesDrawCount;
    ++s_3DData->RendererStats.IndicesCount; // Debug Purpose
}

s_3DData->IndicesCurrentOffset += mesh->m_MaxIndex;

So now each mesh stores the (max index + 1) that it has (for a quad with indices from 0 to 3, this would be 4).

This way, I can go through all mesh indices while updating the indices that we use to draw and then I can update the current offset value so that we properly store all the indices drawn in order.

Again, I'm not intending this to be fast nor performative, I was just learning how to do this (and I did :) ).

The result:

在此处输入图片说明

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