OpenGL 使用单个 VBO 渲染多个对象,使用另一个 VBO 更新对象的矩阵
OpenGL render multiple objects using single VBO and updata object's matrices using another VBO
所以,我需要使用一次绘制调用来渲染多个对象(不是实例)的方法。实际上我知道如何做到这一点,只是将数据放入单个 vbo/ibo 并使用 glDrawElements 进行渲染。
问题是:使用 glUniform 更新统一数据而不为每个对象设置它的有效方法是什么...?
我如何设置一个包含数十个对象的所有统一数据的缓冲区,包括 MVP 矩阵,绑定它并使用单个绘制调用执行渲染?
我尝试使用 UBO,但这根本不是我需要的。
对于渲染实例,我们只是将统一数据(包括矩阵)放在另一个 VBO 中,并使用 glVertexAttribDivisor 设置属性除数,但它仅适用于实例。
有没有办法在 OpenGL 中实现我想要的?如果没有,我可以做些什么来克服为几十个对象设置统一数据的开销?
例如像这样:
{
// setting up VBO
glGenBuffers(1, &vbo);
glBindBuffer(vbo);
glBufferData(..., data_size);
// setup buffer
for(int i = 0; i < objects_num; i++)
glBufferSubData(...offset, size, &(objects[i]));
// the same for IBO
.........
// when setup some buffer, that will store all uniforms, for every object
.........
glDrawElements(...);
}
在此先感谢您的帮助。
如果您同意需要 OpenGL 4.3 或更高版本,我相信您可以使用 glMultiDrawElementsIndirect()
通过单个绘制调用来渲染它。这实际上允许您使用单个 API 调用进行多个绘制调用。每个子调用都由以下形式的结构中的值定义:
typedef struct {
GLuint count;
GLuint instanceCount;
GLuint firstIndex;
GLuint baseVertex;
GLuint baseInstance;
} DrawElementsIndirectCommand;
由于您不想绘制相同顶点的多个实例,因此在每个绘制调用中使用 1 作为 instanceCount
。关键思想是您仍然可以通过为每个实例指定不同的 baseInstance
值来使用实例化。因此每个对象都会有不同的 gl_InstanceID
值,并且您可以使用实例化属性来获取每个对象想要改变的值(矩阵等)。
因此,如果您当前有渲染循环:
for (int k = 0; k < objectCount; ++k) {
// set uniforms for object k.
glDrawElements(GL_TRIANGLES, object[k].indexCount,
GL_UNSIGNED_INT, object[k].indexOffset * sizeof(GLuint));
}
您将改为使用以下参数填充上面定义的结构的数组:
DrawElementsIndirectCommand cmds[objectCount];
for (int k = 0; k < objectCount; ++k) {
cmds[k].count = object[k].indexCount;
cmds[k].instanceCount = 1;
cmds[k].firstIndex = object[k].indexOffset;
cmds[k].baseVertex = 0;
cmds[k].baseInstance = k;
}
// Rest of setup.
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, objectCount, 0);
我没有提供上面完整设置的代码。关键步骤包括:
- 将
cmds
数组放入缓冲区,并将其绑定为 GL_DRAW_INDIRECT_BUFFER
。
- 将每个对象的值存储在 VBO 中。设置相应的顶点属性,包括将它们指定为
glVertexAttribDivisor(1)
. 的实例
- 像往常一样设置每个顶点的属性。
- 照常设置索引缓冲区。
为此,所有对象的索引必须在同一个索引缓冲区中,并且每个属性的值必须在所有对象的同一个 VBO 中。
所以,我需要使用一次绘制调用来渲染多个对象(不是实例)的方法。实际上我知道如何做到这一点,只是将数据放入单个 vbo/ibo 并使用 glDrawElements 进行渲染。
问题是:使用 glUniform 更新统一数据而不为每个对象设置它的有效方法是什么...?
我如何设置一个包含数十个对象的所有统一数据的缓冲区,包括 MVP 矩阵,绑定它并使用单个绘制调用执行渲染? 我尝试使用 UBO,但这根本不是我需要的。
对于渲染实例,我们只是将统一数据(包括矩阵)放在另一个 VBO 中,并使用 glVertexAttribDivisor 设置属性除数,但它仅适用于实例。
有没有办法在 OpenGL 中实现我想要的?如果没有,我可以做些什么来克服为几十个对象设置统一数据的开销?
例如像这样:
{
// setting up VBO
glGenBuffers(1, &vbo);
glBindBuffer(vbo);
glBufferData(..., data_size);
// setup buffer
for(int i = 0; i < objects_num; i++)
glBufferSubData(...offset, size, &(objects[i]));
// the same for IBO
.........
// when setup some buffer, that will store all uniforms, for every object
.........
glDrawElements(...);
}
在此先感谢您的帮助。
如果您同意需要 OpenGL 4.3 或更高版本,我相信您可以使用 glMultiDrawElementsIndirect()
通过单个绘制调用来渲染它。这实际上允许您使用单个 API 调用进行多个绘制调用。每个子调用都由以下形式的结构中的值定义:
typedef struct {
GLuint count;
GLuint instanceCount;
GLuint firstIndex;
GLuint baseVertex;
GLuint baseInstance;
} DrawElementsIndirectCommand;
由于您不想绘制相同顶点的多个实例,因此在每个绘制调用中使用 1 作为 instanceCount
。关键思想是您仍然可以通过为每个实例指定不同的 baseInstance
值来使用实例化。因此每个对象都会有不同的 gl_InstanceID
值,并且您可以使用实例化属性来获取每个对象想要改变的值(矩阵等)。
因此,如果您当前有渲染循环:
for (int k = 0; k < objectCount; ++k) {
// set uniforms for object k.
glDrawElements(GL_TRIANGLES, object[k].indexCount,
GL_UNSIGNED_INT, object[k].indexOffset * sizeof(GLuint));
}
您将改为使用以下参数填充上面定义的结构的数组:
DrawElementsIndirectCommand cmds[objectCount];
for (int k = 0; k < objectCount; ++k) {
cmds[k].count = object[k].indexCount;
cmds[k].instanceCount = 1;
cmds[k].firstIndex = object[k].indexOffset;
cmds[k].baseVertex = 0;
cmds[k].baseInstance = k;
}
// Rest of setup.
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, objectCount, 0);
我没有提供上面完整设置的代码。关键步骤包括:
- 将
cmds
数组放入缓冲区,并将其绑定为GL_DRAW_INDIRECT_BUFFER
。 - 将每个对象的值存储在 VBO 中。设置相应的顶点属性,包括将它们指定为
glVertexAttribDivisor(1)
. 的实例
- 像往常一样设置每个顶点的属性。
- 照常设置索引缓冲区。
为此,所有对象的索引必须在同一个索引缓冲区中,并且每个属性的值必须在所有对象的同一个 VBO 中。