OpenGL Combining multiple cubes into a single mesh

Viewed 45

I am having trouble merging multiple simple objects into a single mesh with OpenGL. Before I go on I am aware of instancing and I'm certain that it's not what I want.

I'm drawing a cube with the following buffers:

    float cubeVertices[] = {
        -0.5f, 0.5f, 0.5f, //front top left
        0.5f, 0.5f, 0.5f, //front top right
        -0.5f, -0.5f, 0.5f, //front bottom left
        0.5f, -0.5f, 0.5f, //front bottom right
        -0.5f, 0.5f, -0.5f, //back top left
        0.5f, 0.5f, -0.5f, //back top right
        -0.5f, -0.5f, -0.5f, //back bottom left
        0.5f, -0.5f, -0.5f //back bottom right
    };
    
    unsigned int cubeIndices[] = {
        0, 2, 1, //FRONT
        1, 2, 3,
        
        0, 1, 4, //TOP
        4, 1, 5,
        
        1, 3, 5, //RIGHT
        5, 3, 7,
        
        2, 7, 3, //BOTTOM
        7, 2, 6,
        
        6, 2, 0, //LEFT
        0, 4, 6,
        
        6, 4, 5, //BACK
        5, 7, 6
    };

I then call glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr) which works as wanted.

Now my problem is if I want to draw multiple cubes in the same draw call (Still don't want instancing). My current way of rendering multiple cubes is by:

for (int y = 0; y < 2; ++y)
        {
            for (int x = 0; x < 2; ++x)
            {
                for (int z = 0; z < 2; ++z)
                {
                    model = glm::mat4{1.0f};
                    model = glm::translate(model, glm::vec3{x, y, z});
                    shader.SetMat4("uModel", model);
                    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr);
                }
            }
        }

Which is fine for a 2x2x2 cube of cubes but if I want to go larger like 32x32x32 it starts to fall apart performance wise.

So I was wondering how I can merge this 2x2x2 cube into a single mesh and call glDrawElements(GL_TRIANGLES, 36 * 8, GL_UNSIGNED_INT, nullptr) or similar.

I've tried:

std::array<glm::vec3, 8> locations{};
    for (int x = 0; x < 2; ++x)
    {
        for (int y = 0; y < 2; ++y)
        {
            for (int z = 0; z < 2; ++z)
            {
                locations[x + 2 * (y + 2 * z)] = glm::vec3{x, y, z};
            }
        }
    }
    
    
    glBindVertexArray(cubeVAO);
    glBindBuffer(GL_ARRAY_BUFFER, cubeVBO[1]);
    glBufferData(GL_ARRAY_BUFFER, locations.size(), locations.data(), GL_DYNAMIC_DRAW);
    
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
    glEnableVertexAttribArray(1);
    glBindVertexArray(0);

And using gl_Position = uProjection * uView * uModel * vec4(aPos * aLocation, 1.0f); where uModel is just a glm::mat4{1.0f}, aPos is from cubeVertices and aLocation is from the locations array.

I've got a couple ideas on why it doesn't work which I'll list.

First off: aLocation is different for every vertex instead of staying constant for the entire cube and then changing to the next location. Not sure how to do that. I could copy and paste the same location for every vertex I assume but that seems to defeat the purpose of using an element buffer in the first place.

Second: cubeIndices[] only holds enough indices for a single cube so I could also fix that by copy pasting the cubeIndices 8 times but doesn't that defeat the purpose of using an element buffer if I just have the same data 8 times? So maybe a double element buffer could work?

I know instancing does seem like a really obvious solution but I guarantee that it's not what I am after.

I'm quite new to OpenGL so I don't have the most solid grasp of what I'm doing yet so I'm just trying to figure OpenGL out by making small projects. If someone could help it would be greatly appreciated.

1 Answers

It really looks like it is instancing that you want.

Do your objects moves but the geometry of each one stays the same? instancing.
Does each object move with bones animations? instancing.
Does the geometry moves every frame and not bone animated? not instancing, dynamic buffers.

One of the bottleneck of GPU is main memory=>GPU data transfers. You will struggle to update a lot of geometry at once because you need to transfer all the vertex every frame (~100 times a sec).

Your 2nd code is good. If you have performance issues its for another reason.
If you try to draw 32*32*32 cubes, that's 6*2*32*32*32=393 216 triangles. That could be a lot depending on your hardware.
If you used a big index buffer, everything would be the same, but you would use 6*3*2*32*32*32*sizeof(UINT)=4.7MB instead of 6*3*2*sizeof(UINT)=144B of memory space for your index buffer.

If the vertex positions are changing, you don't have to update the index buffer, there is probably a way to select a different start vertex. There is in Direct3D but IDK for opengl.

Related