0
votes

I've implemented an OpenGL spritebatch, but it's only drawing the first sprite when using the same texture for multiple sprites. For example: if I use it to render glyphs for a string "Hello", it only renders the "H".

My shaders are very simple:

Fragment:

#version 150 core

uniform sampler2D tex;

in vec2 Texcoord;
out vec4 outColor;

void main()
{
    outColor = texture(tex, Texcoord); 
}

Vertex:

#version 150 core

in vec3 position;
in vec2 texCoords;
out vec2 Texcoord;

void main()
{
    Texcoord = texCoords;
    gl_Position = vec4(position, 1.0f);
}

And they compile, link and validate without any errors.

My sprite batching code is also quite simple. Here's how I initialize the Sprite Batch:

glGenVertexArrays(1, &mVao);
glBindVertexArray(mVao);

    // INDEX BUFFER
glGenBuffers(1, &mEbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);

    // VERTEX BUFFER
glGenBuffers(1, &mVbo);
glBindBuffer(GL_ARRAY_BUFFER, mVbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);

    // POS ATTRIB
GLint posAttrib;
posAttrib = glGetAttribLocation(mShader.getProgram(), "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), 0);

    // TEX ATTRIB
GLint texAttrib;
texAttrib = glGetAttribLocation(mShader.getProgram(), "texCoords");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));

And finally, here is how I draw:

glBindVertexArray(mVao);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STREAM_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, mVbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STREAM_DRAW);

glDrawElements(GL_TRIANGLES, (GLsizei)indices.size(), GL_UNSIGNED_INT, 0);

glBindVertexArray(0);

OpenGL throws no errors, vertices and indices contain the correct data, as far as I can tell.

I'm sorry for the amount of code, but because I get no errors whatsoever, I have no indication of where it goes wrong. When I debug the code, I see no values that are out of place or wrong.

EDIT

The code I use to set up the vertices and indices vectors is this:

for(int i = 0; i < currentSprites.size(); ++i)
{
   Sprite* current = currentSprites.at(i);

    // 4 * (2 (pos) + 2 (tex))
    float verticesArray[16] =
    {
         // TOP LEFT
         current->x,
         current->y,
         (float)current->textureRect.x / (float)current->texture.w,
         (float)current->textureRect.y / (float)current->texture.h,
         // TOP RIGHT
         current->x + current->w,
         current->y,
         (float)(current->textureRect.x + current->textureRect.w) / (float)current->texture.w,
         (float)current->textureRect.y / (float)current->texture.h,
         // BOTTOM RIGHT
         current->x + current->w,
         current->y + current->h,
         (float)(current->textureRect.x + current->textureRect.w) / (float)current->texture.w,
         (float)(current->textureRect.y + current->textureRect.h) / (float)current->texture.h,
         // BOTTOM LEFT
         current->x,
         current->y + current->h,
         (float)current->textureRect.x / (float)current->texture.w,
         (float)(current->textureRect.y + current->textureRect.h) / (float)current->texture.h
    };

    for(int v = 0; v < 16; v += 4)
    {

        float zPos = -(float)current->layer / 100.0f;

        // Projection matrix
        glm::vec4 result = mCamera.getProjectionMatrix() * glm::vec4(verticesArray[v], verticesArray[v+1], zPos, 1.0f); // This is just an orthographic projection matrix
        verticesArray[v] = result.x;
        verticesArray[v+1] = result.y;
        zPos = result.z;

        vertices.push_back(Vertex(glm::vec3(verticesArray[v], verticesArray[v + 1], zPos),
                                  glm::vec2(verticesArray[v + 2], verticesArray[v + 3])));
    }

    static GLuint _indices[6] =
    {
    0, 1, 3, 3, 1, 2
    };

    for(int index = 0; index < 6; ++index)
    {
        indices.push_back(_indices[index]);
    }
}
1
The code posted so far looks OK. I think we need to see at least the code which setups the indices and vertices arrays. - derhass
@derhass I've added that code. I hope it's not too much. First part just sets up vertices and texture coordinates, then it transforms the vertices to the OpenGL clip space coordinate system. - RaptorDotCpp

1 Answers

2
votes

I think the issue is your index buffer. You are batching the vertices for all glyphs into the same VBO, and draw them with one draw call (which is a good approach). However, for each glypgh, you are adding the same 6 indices:

static GLuint _indices[6] =
{
0, 1, 3, 3, 1, 2
};
for(int index = 0; index < 6; ++index)
{
    indices.push_back(_indices[index]);
}

So, in effect, your code will draw the first quad over and over again, never using any of the vertices you added after that.

You need to offset the indices for each glyph you are drawing, like:

for(int index = 0; index < 6; ++index)
{
    indices.push_back(_indices[index] + 4*i);
}