Entalpi
Entalpi

Reputation: 2033

Draw thousands of primitives with one draw call

I want to render 100,000+ number of cubes (made of 36 vertices, using indexed geometry in the same VAO) with one draw call, is this possible?

All the geometry data (vertice data) is the same, since they are all cubes. The projection and camera view is also the same and passed to the vertex shader as uniforms.


Render.cpp

for (auto chunk : world->chunks) {
    for (auto cube : chunk.blocks) {
        glBindTexture(GL_TEXTURE_CUBE_MAP, textures[cube.texture]);

        // Model - transform_z * transform_y * transform_x * transform_translation * transform_scaling
        model = Mat4<GLfloat>();
        model = model.translate(cube.position);
        model = model.scale(cube.scale);
        model = model * transformation_matrix_x(cube.theta_x);
        model = model * transformation_matrix_y(cube.theta_y);
        model = model * transformation_matrix_z(cube.theta_z);
        glUniformMatrix4fv(gl_model, 1, GL_TRUE, model.data());
        // One draw call per cube is not scalable
        glDrawElements(GL_TRIANGLES, cube.indices.size(), GL_UNSIGNED_INT, 0);
    }
    // A draw call like this would be nice since they share so much data.
    // glDrawLotsOfElements(GL_TRIANGLES, cube.indices.size() * numCubes, GL_UNSIGNED_INT, 0);
}

Vertex shader

#version 330 core

in vec3 position;
in vec4 vColor;

out vec4 fColor;
out vec3 fTexcoord;

// Model
uniform mat4 model;

// View or a.k.a camera matrix
uniform mat4 camera_view;

// Projection 
uniform mat4 projection;

void main() {
  gl_Position = projection * camera_view * model * vec4(position, 1.0f);
  fColor = vColor;
  fTexcoord = normalize(position);
}

The number of cubes are dynamic, if that matters.

Upvotes: 1

Views: 870

Answers (1)

Riggs
Riggs

Reputation: 119

I used instanced rendering a while ago when rendering particles, so we'll see if this can point you in the right direction.

Before your game loop you will call a function where you render your scene. In the function you can setup the model matrices like this, so you can have each cube at a different position in the world:

//matrices for the cubes
//set amount by the number of cubes you want to draw
glm::mat4* modelMatrices;
modelMatrices = new glm::mat4[amount];

You will then enter a loop where you will give each cube a different position

for (GLuint i = 0; i < amount; i++)
{    //use a function like rand() to get the x,y,z values different
     glm::mat4 model;
     model = glm::translate(model, glm::vec3(x, y, z));
     modelMatrices[i] = model;
}

Now it looks a bit different when filling the buffers, because you have to send the model matrices (mat4) as an attribute to the GPU side, so you will need 4 vertex attribute pointers for that, and also don't forget

        glVertexAttribDivisor(1, 1);
        glVertexAttribDivisor(2, 1);
        glVertexAttribDivisor(3, 1);
        glVertexAttribDivisor(4, 1);

and at last

glDrawArraysInstanced(GL_POINTS, 0, 1, amount); //replace to specify for a cube

Then you will have to use glDrawArraysInstanced one more time when you actually render the cubes.

On the GPU side you only have to replace the usual model matrix with the one you sent before.

Upvotes: 1

Related Questions