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

OpenGL单个VAO关联多对象独立VBO的绘制实现问询

核心问题:单个VAO关联多个独立Buffer实现多对象绘制

问题背景

我尝试将多个Buffer关联到单个Vertex Array Object (VAO),每个Buffer对应一个对象模型(比如顶点、法线、纹理数据)。为简化示例,我创建了两个仅含单个三角形(3个顶点)及对应颜色的对象:ArrayBuffer1存储第一个三角形的齐次顶点坐标与RGBA颜色向量,ArrayBuffer2存储第二个三角形的对应数据。

着色器代码

顶点着色器

#version 400 core

layout( location = 0 ) in vec4 vPosition;
layout( location = 1 ) in vec4 vColor;
out vec4 color;
void
main()
{
    color= vColor;
    gl_Position = vPosition;
}

片段着色器

#version 450 core

in vec4 color;
out vec4 fColor;

void main()
{
    fColor = color; 
}

客户端初始化与渲染代码

初始化代码

enum VertexDim { xDim, yDim, zDim, wDim, NumDim};
enum VAO_IDs { Triangles, NumVAOs };
enum Buffer_IDs { ArrayBuffer1, ArrayBuffer2, NumBuffers };
enum Attrib_IDs { vPosition = 0, vColor=1 };

GLuint  VAOs[NumVAOs];
GLuint  Buffers[NumBuffers];

const GLuint  NumVertices = 6;
const GLuint curDim= NumDim;
// first triangle 
GLfloat  static_vertices1[NumVertices/2][NumDim] = {
    { -0.90f, -0.90f, 0.5f, 1}, {  0.85f, -0.90f, 0.3f, 1 }, { -0.90f,  0.85f, 0.0f, 1 }  // Triangle 1
};
// second triangle
GLfloat  static_vertices2[NumVertices/2][NumDim] = {
    {  0.90f, -0.85f, -0.1f, 1 }, {  0.90f,  0.90f, -0.5f, 1 }, { -0.85f,  0.90f, -0.8f, 1 }   // Triangle 2
};
// a base color from which to compute different vertex colors
GLfloat infColor_value[4]= {0.9f, 0.7f, 0.5f, 1.0f};  

//----------------------------------------------------------------------------
//
// init
//

void
init( void )
{   
    const unsigned int n=NumVertices/2;
    GLfloat infColor_values1[n][curDim];    
    GLfloat infColor_values2[n][curDim];  
    // compute different vertex colors from base color   infColor_value
    // .. loop over    infColor_values1 and infColor_values2

    ShaderInfo  shaders[] =
    {
        { GL_VERTEX_SHADER, "media/shaders/triangles/triangles2.vert" },
        { GL_FRAGMENT_SHADER, "media/shaders/triangles/triangles2.frag" },
        { GL_NONE, NULL }
    };
    // LoadShaders is a wrapper to load, compile code and link shaders
    GLuint program = LoadShaders( shaders );
    if( program == 0) { std::cerr<<"shader program failed to build" << std::endl; }
    glUseProgram( program );

    glGenVertexArrays( NumVAOs, VAOs );
    glBindVertexArray( VAOs[Triangles] );
    glCreateBuffers( NumBuffers, Buffers );

    // vertex 1
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer1] );
    glNamedBufferData( Buffers[ArrayBuffer1], sizeof(static_vertices1)+sizeof(infColor_values1) , NULL, GL_STATIC_DRAW);
    glNamedBufferSubData( Buffers[ArrayBuffer1], 0,  sizeof(static_vertices1), static_vertices1);
    glNamedBufferSubData( Buffers[ArrayBuffer1], sizeof(static_vertices1),  sizeof(infColor_values1), infColor_values1);

    GLuint64 offset = 0;
    glVertexAttribPointer( vPosition, NumDim, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
    offset = sizeof(static_vertices1);
    glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)offset );
    glEnableVertexAttribArray( vColor );

    // vertex 2 
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer2] );       
    glNamedBufferData( Buffers[ArrayBuffer2], sizeof(static_vertices2)+sizeof(infColor_values2) , NULL, GL_STATIC_DRAW);
    glNamedBufferSubData( Buffers[ArrayBuffer2], 0,  sizeof(static_vertices2), static_vertices1);
    glNamedBufferSubData( Buffers[ArrayBuffer2], sizeof(static_vertices2),  sizeof(infColor_values2), infColor_values2);

    offset = 0;
    glVertexAttribPointer( vPosition, NumDim, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
    offset = sizeof(static_vertices2);
    glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)offset );
    glEnableVertexAttribArray( vColor );
    
    glBindBuffer( GL_ARRAY_BUFFER, 0 );
    glBindVertexArray(0);
}

渲染代码

void
display( void )
{
    static const float black[] = { 0.0f, 0.0f, 0.0f, 0.0f };

    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );
    glDrawArrays( GL_TRIANGLES,  0, NumVertices );
    glBindVertexArray(0);    
}

核心疑问

如何将每个对象的顶点属性存储在独立的单个Buffer中,同时实现多个对象的正确绘制?我可以将所有数据放入同一子缓冲,但无法找到分离对象渲染的方案,且未在相关资料中找到针对性答案。


问题分析与解决方案

你的代码存在两个关键问题:

  • VAO状态覆盖:单个VAO只能记录一组顶点属性配置。你在初始化时先绑定ArrayBuffer1并配置vPosition和vColor,之后又绑定ArrayBuffer2重新配置这两个属性,这会覆盖VAO中之前的配置。最终VAO只关联了ArrayBuffer2的属性,且你还写错了static_vertices1作为第二个Buffer的顶点数据(应该是static_vertices2)。
  • 绘制逻辑错误:单个glDrawArrays无法同时使用两个不同的Buffer数据,因为VAO只能绑定一组属性对应的Buffer。

方案一:为每个对象创建独立VAO(推荐)

每个对象对应一个VAO,各自绑定自己的Buffer并配置顶点属性,渲染时分别绑定对应的VAO并绘制:

// 修改枚举定义
enum VAO_IDs { Triangle1, Triangle2, NumVAOs };
GLuint  VAOs[NumVAOs];

// 初始化流程调整
glGenVertexArrays( NumVAOs, VAOs );

// 初始化第一个三角形
glBindVertexArray( VAOs[Triangle1] );
glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer1] );
glVertexAttribPointer( vPosition, NumDim, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
glEnableVertexAttribArray( vPosition );
GLuint64 offset = sizeof(static_vertices1);
glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)offset );
glEnableVertexAttribArray( vColor );

// 初始化第二个三角形
glBindVertexArray( VAOs[Triangle2] );
glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer2] );
// 修正数据错误:传入static_vertices2而非static_vertices1
glNamedBufferSubData( Buffers[ArrayBuffer2], 0,  sizeof(static_vertices2), static_vertices2);
glVertexAttribPointer( vPosition, NumDim, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
glEnableVertexAttribArray( vPosition );
offset = sizeof(static_vertices2);
glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)offset );
glEnableVertexAttribArray( vColor );

// 渲染函数修改
void display(void) {
    static const float black[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangle1] );
    glDrawArrays( GL_TRIANGLES, 0, 3 );

    glBindVertexArray( VAOs[Triangle2] );
    glDrawArrays( GL_TRIANGLES, 0, 3 );

    glBindVertexArray(0);
}

方案二:使用单个VAO,渲染时切换Buffer并重新配置属性(不推荐)

如果坚持用单个VAO,需要在渲染时分别绑定每个对象的Buffer,重新配置顶点属性后再绘制:

void display(void) {
    static const float black[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    glClearBufferfv(GL_COLOR, 0, black);

    glBindVertexArray( VAOs[Triangles] );

    // 绘制第一个三角形
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer1] );
    glVertexAttribPointer( vPosition, NumDim, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
    GLuint64 offset = sizeof(static_vertices1);
    glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)offset );
    glEnableVertexAttribArray( vColor );
    glDrawArrays( GL_TRIANGLES, 0, 3 );

    // 绘制第二个三角形
    glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer2] );
    // 修正数据错误:确保Buffer内是static_vertices2的数据
    glVertexAttribPointer( vPosition, NumDim, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
    glEnableVertexAttribArray( vPosition );
    offset = sizeof(static_vertices2);
    glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)offset );
    glEnableVertexAttribArray( vColor );
    glDrawArrays( GL_TRIANGLES, 0, 3 );

    glBindVertexArray(0);
}

这种方案效率较低,因为每次绘制都要重新配置顶点属性,违背了VAO的设计初衷,所以优先推荐方案一。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 17:01:05