0

I want to draw a selected number of instances using glDrawElements... but I don't know which function to use and how to set the parameters.

I set the gl_InstanceID in shader and set the shader data through SSBO:

glGenBuffers(1, &SSBO);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, data_vector.size() * sizeof(data_type), data_vector.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding_index, SSBO);

I have an array drawable_vector contains bool to check if the vertex is drawn or not.

for(auto index = 0; index  < drawable_vector.size(); index ++){
    glDrawElements...(???)
}

I want to draw instance with ID gl_instanceID only when the drawable_vector[index] is true. The size of drawable_vector, data_vector and number of instances are the same.

Edit: Setup & render code

Setup:

//VAO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);

//VBO1 - Position
glGenBuffers(1, &VBO1);
glBindBuffer(GL_ARRAY_BUFFER, VBO1);
glBufferData(GL_ARRAY_BUFFER, 3*sizeof(glm::vec4), vertices.data(), GL_DYNAMIC_DRAW);

//VBO2 - color
glGenBuffers(1, &VBO2);
glBindBuffer(GL_ARRAY_BUFFER, VBO2);
glBufferData(GL_ARRAY_BUFFER, 3*sizeof(glm::vec4), colors.data(), GL_DYNAMIC_DRAW);

//EBO
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);

//SSBO - models in shader
glGenBuffers(1, &SSBO);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, models.size() * sizeof(glm::vec4), models.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, SSBO);

//Set data of shader
//in_pos
glBindBuffer(GL_ARRAY_BUFFER, VBO1);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (void *) 0);
glEnableVertexAttribArray(0);
//in_color
glBindBuffer(GL_ARRAY_BUFFER, VBO2);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void *) 0);
glEnableVertexAttribArray(1);

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glBindVertexArray(0);

Shader:

#version 450 core
out vec4 frag_color;

in vec4 out_color;

void main()
{
    frag_color = out_color;
}
#version 430 core
layout (location = 0) in vec4 in_pos;
layout (location = 1) in vec4 in_color;
layout(std430, binding = 2) buffer DataBuffer
{
    mat4 in_models[];
} buffer_data;

uniform mat4 view;
uniform mat4 projection;

out vec4 out_color;

void main()
{
    out_color = in_color;
    gl_Position = projection * view * buffer_data.in_models[gl_InstanceID] * in_pos;

}

Render:

use_shader();
shader_set_view("view", view);
shader_set_projection("projection", projection);
update_models();
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO1);
glBindBuffer(GL_ARRAY_BUFFER, VBO2);
glBindBuffer(GL_ARRAY_BUFFER, EBO);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, SSBO[0]);
glDrawArraysInstanced(GL_TRIANGLES, 0, 3, n_instances);

glfwSwapBuffers(window);
glBindVertexArray(0);
KennyTan
  • 73
  • 1
  • 8

2 Answers2

1

You cannot quite discard individual instances on the GPU in a way you imagine. You shall instead aggregate the IDs of the instances you want to render, and issue rendering commands for those instances.

The command that can draw a specific instance is glDrawElementsInstancedBaseInstance, which you can call in a loop:

for(auto instance = 0; instance < drawable_vector.size(); instance++)
    if(drawable_vector[instance])
        glDrawElementsInstancedBaseInstance(mode, count, type, indices, 1 /* one instance */, instance);

You can also combine all these calls to a single call to glDrawElementsIndirect by packing those parameters to a GL_DRAW_INDIRECT_BUFFER:

vector<DrawElementsIndirectCommand> draw_indirect;
for(auto instance = 0; instance < drawable_vector.size(); instance++)
    if(drawable_vector[instance])
        draw_indirect.push_back({count, 1 /* one instance */, firstIndex, baseVertex, instance});

//glGenBuffers(1, &draw_indirect_buf); // do once during initialization
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, draw_indirect_buf);
glBufferData(GL_DRAW_INDIRECT_BUFFER, draw_indirect.size() * sizeof(DrawElementsIndirectCommand), draw_indirect_buf.data(), GL_DYNAMIC_DRAW);
glDrawElementsIndirect(mode, type, draw_indirect.size(), 0);

The content of the GL_DRAW_INDIRECT_BUFFER can also be generated directly on the GPU in a compute shader. This would require uploading drawable_vector instead of draw_indirect, and that's unlikely to be any better than the above code.

EDIT: Since you're fetching the instance data through an SSBO rather than a VAO, you would need to adjust your instance id calculation. The formula that OpenGL uses is:

gl_InstanceID/divisor + gl_BaseInstance

Notice that gl_InstanceID doesn't include the base-instance that we specify in each of the draw calls/commands, so it's going to be zero. Therefore your shader should use gl_BaseInstance instead, or a combination thereof:

gl_Position = projection * view * buffer_data.in_models[gl_BaseInstance + gl_InstanceID] * in_pos;

However, gl_BaseInstance was introduced in the ARB_shader_draw_parameters extension, and was made core in OpenGL 4.6, so you may not have it available. Without gl_BaseInstance you cannot know the actual instance from within the shader when using the multi-draw or base-instance APIs. If you're stuck with an older OpenGL version, you'll need to resort to passing your MVP matrices through VAO instance attributes instead of an SSBO.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • Thanks. I tried `glDrawElementsInstancedBaseInstance(GL_TRIANGLES,3,GL_UNSIGNED_INT,(void*)0,1,instance)` but it shows only 1 object. Even with two calls it's still showing 1 object. Do I need to setup anything else? – KennyTan Jun 21 '23 at 03:43
  • @KennyTan your shaders and VAO should be set up correctly too -- but you didn't include those in the question. – Yakov Galka Jun 21 '23 at 13:04
  • @KennyTan are you using `gl_InstanceID` to fetch the instance data from an SSBO? Beware that t doesn't include the base-instance -- so all `gl_InstanceID` will be zero; you shall use `gl_BaseInstance + gl_InstanceID` to determine the actual instance. – Yakov Galka Jun 21 '23 at 13:23
  • I can draw all instances with `glDrawArraysInstanced` so the shaders and VAO are set up correctly. What is the `gl_BaseInstance` anyway? is that the `binding_index` in the SSBO setup? – KennyTan Jun 21 '23 at 14:56
  • @KennyTan it may be "set up correctly" for the simple case of `glDrawArraysInstanced`, but not correct enough for the general case. `glDrawArraysInstanced` assumes `gl_BaseInstance == 0` and `gl_InstanceID` will go through `0, 1, ..., instanceCount-1`. With this setup, however, `gl_BaseInstance` will get values from the `0, 1, ..., instanceCount-1` set, and `gl_InstanceID` will be zero. OpenGL uses `gl_BaseInstance + gl_InstanceID` for fetching instanced attributes from a VAO, but you need to take `gl_BaseInstance` into account if you fetch your vertex data manually from an SSBO. – Yakov Galka Jun 21 '23 at 15:50
  • No, it's not related to `binding_index` in any way. – Yakov Galka Jun 21 '23 at 15:51
  • I just update the code in the questions. Can you check if I'm doing correctly? Many thanks @Yakov Galka. – KennyTan Jun 21 '23 at 16:16
  • Thanks @Yakoc Galka for your guide, I managed to make it. – KennyTan Jun 25 '23 at 16:28
0

I figured it out.

Hav to use the command @Yakov Galka mentioned and added gl_BaseInstance to the shader

void main()
{
    out_color = in_color;
    gl_Position = projection * view * buffer_data.in_models[gl_BaseInstance+gl_InstanceID] * in_pos;

}

and set baseinstance in glDrawElementsInstancedBaseInstance to instance numbers to draw.

for(int r_index = 0; r_index < instance_id_list; r_index++) {
    glDrawElementsInstancedBaseInstance(
            GL_TRIANGLES,      // type of primitive to render
            3,                 // vertex count
            GL_UNSIGNED_INT,   // type of each index in the GL_ELEMENT_ARRAY_BUFFER
            (void*)0,          // element array buffer offset
            1,                 // Number of copies to render
            instance_id_list[r_index]  // Number to start from for InstanceId
    );
}
KennyTan
  • 73
  • 1
  • 8