-1

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?

genpfault
  • 51,148
  • 11
  • 85
  • 139
Samuel
  • 315
  • 3
  • 14
  • @VictorGubin First of all, thanks a lot for your kindness. 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 rendered on the bottom-left. I have to get 4 of the same textures rendered at different coordinates. Do you have any idea why I am getting this weird behaviour? Thanks :) – Samuel Mar 26 '20 at 16:05
  • 1
    @VictorGubin I fixed the problem.. Thank you very much! The problem was that vb_size and ib_size was assigned to an incorrect size.. Since OpenGL takes a bytesize, I had to multiply the value by sizeof(GLfloat) – Samuel Mar 26 '20 at 17:52

4 Answers4

2

Let's start with this code:

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

No idea about your particular Buffer Object abstraction class, but glBufferData in GL expects the size in bytes, so

VBO.BufferData(vb_size, ...);
IBO.BufferData(ib_size, ...);

will be uploading only 1/4th of your actual data (matching exactly the first of your four objects by accident).

derhass
  • 43,833
  • 2
  • 57
  • 78
1

All your objects have the same texture coordinates. Since you are writing a batch renderer, all your objects are rendered with a single draw call which means a single texture (at least, looking at your code, that seems to be your approch, since you are only binding a single texture and there is no code in your shader to use any other texture than the single one currently bound).

So I assume you have ONE texture that consists of four different images? If that's the case you should get your 4 objects but every object shows all four images in that one texture. To fix it, you have to adjust your texture coordinates like so:

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},
            {0.5f, 0.5f,  0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f}};

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},
                    {0.5, 1.0f,  0.5f, 0.5f, 0.0f, 0.5f, 0.0f, 1.0f}};

And so on. I haven't checked those but this should be the cause for your problem.

Another option is to bind multiple textures and give every object some kind of id which you can access in your fragment shader to determine what texture unit to use. In this case you have some more work to do in your setup code and fragment shader.

alektron
  • 87
  • 1
  • 10
  • 1
    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? – Samuel Mar 26 '20 at 16:04
  • I see, so it doesn't "render a single TEXTURE" like you said, it renders a single OBJECT. This is odd though because I can't find anything wrong with your code either. Triangle windings seems okay, so culling is not the issue. The number of indices passed to DrawElements seems correct as well. Same with your indices values themselfes. I'd suggest debug your application with a tool such as https://renderdoc.org/ and see if you can find anything there. Also if your ClearColor is set to black try changing it, just in case the missing objects are rendered black as well. – alektron Mar 26 '20 at 16:22
  • I fixed the problem.. Thank you very much! The problem was that vb_size and ib_size was assigned to an incorrect size.. Since OpenGL takes a bytesize, I had to multiply the value by sizeof(GLfloat) – Samuel Mar 26 '20 at 17:50
0

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.

Samuel
  • 315
  • 3
  • 14
0

I got the problem. It was that opengl takes in byte sizes. As Samuel pointed out..

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);
Samuel
  • 315
  • 3
  • 14