Search code examples
c++openglopengl-3opengl-4

When I call glDrawElements, I am getting weird behaviour


I was writing a basic texture batch renderer but somehow, it only renders a single texture rather than four. Is there anything wrong with the below code? I can't find anything wrong with it. I am using GLEW and GLFW for windowing.

The VertexBuffer, VertexArray, IndexBuffer, Shader and Texture classes have been tested and they work correctly.

void BatchRenderObjects(vector<ObjectInstance> texq, GLuint tex_id, Shader shader)
{
int current_index = 0;
int array_index = 0;
const size_t vb_size = 20 * (texq.size());
const size_t ib_size = 6 * texq.size();
const size_t texslot_size = 32;

VertexBuffer VBO(GL_ARRAY_BUFFER);
IndexBuffer IBO;
VertexArray VAO;

GLfloat *vertex_buffer = new GLfloat[vb_size];
GLuint *index_buffer = new GLuint[ib_size];

{
    unsigned int offset = 0;
    for (int i = 0; i < ib_size; i += 6)
    {
        index_buffer[i + 0] = 0 + offset;
        index_buffer[i + 1] = 1 + offset;
        index_buffer[i + 2] = 2 + offset;
        index_buffer[i + 3] = 2 + offset;
        index_buffer[i + 4] = 3 + offset;
        index_buffer[i + 5] = 0 + offset;

        offset += 4;
    }
}

{
    for (int i = 0; i < texq.size(); i++)
    {
        vertex_buffer[current_index + 0] = texq[i].coords[0];
        vertex_buffer[current_index + 1] = texq[i].coords[1];
        vertex_buffer[current_index + 2] = texq[i].coords[2];
        vertex_buffer[current_index + 3] = texq[i].tex_coords[0];
        vertex_buffer[current_index + 4] = texq[i].tex_coords[1];

        vertex_buffer[current_index + 5] = texq[i].coords[3];
        vertex_buffer[current_index + 6] = texq[i].coords[4];
        vertex_buffer[current_index + 7] = texq[i].coords[5];
        vertex_buffer[current_index + 8] = texq[i].tex_coords[2];
        vertex_buffer[current_index + 9] = texq[i].tex_coords[3];

        vertex_buffer[current_index + 10] = texq[i].coords[6];
        vertex_buffer[current_index + 11] = texq[i].coords[7];
        vertex_buffer[current_index + 12] = texq[i].coords[8];
        vertex_buffer[current_index + 13] = texq[i].tex_coords[4];
        vertex_buffer[current_index + 14] = texq[i].tex_coords[5];

        vertex_buffer[current_index + 15] = texq[i].coords[9];
        vertex_buffer[current_index + 16] = texq[i].coords[10];
        vertex_buffer[current_index + 17] = texq[i].coords[11];
        vertex_buffer[current_index + 18] = texq[i].tex_coords[6];
        vertex_buffer[current_index + 19] = texq[i].tex_coords[7];

        current_index = current_index + 20;
    }
}

// Setup vertex buffer, index buffer and vertex array
{
    VAO.Bind();
    VBO.BufferData(vb_size, vertex_buffer, GL_STATIC_DRAW);
    VBO.VertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    VBO.VertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    IBO.BufferData(ib_size, index_buffer, GL_STATIC_DRAW);
    VAO.Unbind();
}

shader.Use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id);
glUniform1i(glGetUniformLocation(shader.GetProgramID(), "u_Texture"), 0);
VAO.Bind();
glDrawElements(GL_TRIANGLES, 6 * texq.size(), GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_TRIANGLES, 0, 24); 
VAO.Unbind();

return; 
}

It's actually very basic. It generates a Vertex Buffer and Index Buffer from a structure and draws the texture on the screen. Here is the ObjectInstance structure.

struct  ObjectInstance
{
    float coords[12]; 
    float tex_coords[8];
};

And finally here is the snippet of my main function.

ObjectInstance t1 = { {-1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
            {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

ObjectInstance t2 = { {-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f},
                    {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

ObjectInstance t3 = { {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f},
                {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

ObjectInstance t4 = { {0.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
                {1.0f, 1.0f,  1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}};

std::vector<ObjectInstance> test_vector;

test_vector.push_back(t1);
test_vector.push_back(t2);
test_vector.push_back(t3);
test_vector.push_back(t4);

I call the function with the appropriate arguments in the game loop.

Lastly here is my vertex and fragment shader. VERTEX SHADER :

#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;

out vec2 TexCoord;

void main()
{
    gl_Position = vec4(position, 1.0f);
    TexCoord = texCoord; 
}

FRAGMENT SHADER

#version 330 core

in vec2 TexCoord;
out vec4 color; // the color outputted

uniform sampler2D u_Texture;

void main()
{
    color = texture(u_Texture, TexCoord); 
}

No, I have one texture that I want to draw 4 times. It is just a single texture that I want to draw in different coordinates of the screen. But, apparently I am only getting one of those printed on the bottom-left. Do you have any idea why I am getting this error?


Solution

  • Hey here is the problem.

    const size_t vb_size = 20 * (texq.size());
    const size_t ib_size = 6 * texq.size();
    

    Should be

    const size_t vb_size = (20 * (texq.size())) * sizeof(GLfloat); 
    const size_t ib_size = (6 * texq.size()) * sizeof(GLfloat);
    

    This is because OpenGL takes the size in raw bytes.