You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用gl_InstanceID与glDrawElements绘制选中的OpenGL实例?

问题描述

我希望使用glDrawElements系列函数绘制选中的实例,但不清楚应选用哪个函数及如何设置参数。

已在着色器中使用gl_InstanceID,并通过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);

我有一个bool类型数组drawable_vector,用于判断对应实例是否需要绘制:

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

要求仅当drawable_vector[index]为true时,绘制gl_InstanceID为index的实例,drawable_vector、data_vector的大小与实例总数一致。

补充代码如下:

初始化代码

//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);

着色器代码

片段着色器

#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;

}

渲染代码

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);

解决方案

针对需求,提供三种不同场景的实现方案:

方案1:逐个绘制选中实例(简单直接)

使用glDrawElementsInstancedBaseInstance函数,指定起始实例ID,每次仅绘制1个选中的实例:

glBindVertexArray(VAO);
for (size_t index = 0; index < drawable_vector.size(); ++index) {
    if (drawable_vector[index]) {
        // 参数说明:图元类型、索引数量、索引类型、索引偏移、实例数量、起始实例ID
        glDrawElementsInstancedBaseInstance(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0, 1, index);
    }
}

注意:该方案会产生大量Draw Call,仅适合实例数量较少的场景。

方案2:批量绘制选中实例(高性能)

通过收集选中的实例ID,配合SSBO批量绘制,仅需一次Draw Call:

  1. 先收集所有需要绘制的实例ID:
std::vector<GLuint> selected_instances;
for (size_t index = 0; index < drawable_vector.size(); ++index) {
    if (drawable_vector[index]) {
        selected_instances.push_back(index);
    }
}
  1. 修改顶点着色器,新增一个SSBO存储选中的实例ID:
#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;
// 新增选中实例ID的SSBO
layout(std430, binding = 3) buffer SelectedInstances {
    uint instance_ids[];
} selected_data;

uniform mat4 view;
uniform mat4 projection;

out vec4 out_color;

void main()
{
    // 通过gl_InstanceID获取实际要绘制的实例ID
    uint actual_id = selected_data.instance_ids[gl_InstanceID];
    out_color = in_color;
    gl_Position = projection * view * buffer_data.in_models[actual_id] * in_pos;
}
  1. 渲染时更新选中实例的SSBO并批量绘制:
// 创建并绑定选中实例的SSBO
GLuint selected_SSBO;
glGenBuffers(1, &selected_SSBO);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, selected_SSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, selected_instances.size() * sizeof(GLuint), selected_instances.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, selected_SSBO);

// 批量绘制所有选中实例
glBindVertexArray(VAO);
glDrawElementsInstanced(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0, selected_instances.size());

方案3:着色器层面剔除(无需修改Draw Call)

将drawable_vector传入SSBO,在顶点着色器中直接剔除不需要绘制的实例:

  1. 修改顶点着色器,新增存储绘制状态的SSBO:
#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;
// 新增绘制状态的SSBO
layout(std430, binding = 3) buffer DrawableBuffer {
    bool drawable[];
} drawable_data;

uniform mat4 view;
uniform mat4 projection;

out vec4 out_color;

void main()
{
    // 剔除不需要绘制的实例:将顶点移到裁剪空间外
    if (!drawable_data.drawable[gl_InstanceID]) {
        gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
        return;
    }
    out_color = in_color;
    gl_Position = projection * view * buffer_data.in_models[gl_InstanceID] * in_pos;
}
  1. 渲染时更新绘制状态的SSBO,直接绘制所有实例:
// 创建并绑定绘制状态的SSBO
GLuint drawable_SSBO;
glGenBuffers(1, &drawable_SSBO);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, drawable_SSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, drawable_vector.size() * sizeof(bool), drawable_vector.data(), GL_DYNAMIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, drawable_SSBO);

// 一次绘制所有实例,着色器自动剔除不需要的部分
glBindVertexArray(VAO);
glDrawElementsInstanced(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0, drawable_vector.size());

补充修正

你的渲染代码中有两处错误需要修正:

  1. glBindBuffer(GL_ARRAY_BUFFER, EBO);:EBO应绑定到GL_ELEMENT_ARRAY_BUFFER,且VAO已保存EBO绑定状态,渲染时无需重复绑定。
  2. glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, SSBO[0]);:SSBO是单个缓冲区ID,应改为SSBO。

内容的提问来源于stack exchange,提问作者KennyTan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:17:19