批处理渲染器 (OpenGL) 的索引问题
Posted
技术标签:
【中文标题】批处理渲染器 (OpenGL) 的索引问题【英文标题】:Indices Problem with a Batch Renderer (OpenGL) 【发布时间】:2021-07-12 23:41:28 【问题描述】:我正在尝试在我正在做的引擎中实现 3D 对象的批量渲染,但我无法使索引正常。
因此,在 3D 渲染器类中,我有一个 Renderer3DData
结构,如下所示:
static const uint MaxQuads = 20000;
static const uint MaxVertices = MaxQuads * 4;
static const uint MaxIndices = MaxQuads * 6;
uint IndicesDrawCount = 0; // Debug var
std::vector<uint> Indices;
Ref<IndexBuffer> IBuffer = nullptr;
// Other data like a VBuffer, VArray...
因此,Indices
的向量将存储要在每个批次上绘制的索引,而 IBuffer
是处理所有 OpenGL 操作的索引缓冲区类(“Ref”是用于创建共享指针的 typedef)。
然后在init函数中初始化一个static Renderer3DData* s_3DData;
,并初始化索引缓冲区如下:
uint* indices = new uint[s_3DData->MaxIndices];
s_3DData->IBuffer = IndexBuffer::Create(indices, s_3DData->MaxIndices);
然后与顶点数组和顶点缓冲区绑定在一起,初始化过程正确完成,因为无需批处理。
因此,在每个新批次上,VArray 都会被绑定,Indices
向量会被清除,并且在绘制的每个网格上,都会像这样修改它:
uint offset = 0;
std::vector<uint> indices = mesh->m_Indices;
for (uint i = 0; i < indices.size(); i += 6)
s_3DData->Indices.push_back(offset + 0 + indices[i]);
s_3DData->Indices.push_back(offset + 1 + indices[i]);
s_3DData->Indices.push_back(offset + 2 + indices[i]);
s_3DData->Indices.push_back(offset + 3 + indices[i]);
s_3DData->Indices.push_back(offset + 4 + indices[i]);
s_3DData->Indices.push_back(offset + 5 + indices[i]);
offset += 4;
s_3DData->IndicesDrawCount += 6;
我不知道我是如何想出这种设置索引缓冲区的方法的,我正在测试这样做的东西,只推送索引或索引 + 偏移量都不起作用。最后,在每次抽奖时,我都会做下一个:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, BufferID);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s_3DData->Indices.size(), s_3DData->Indices.data());
// With the vArray bound:
glDrawElements(GL_TRIANGLES, s_3DData->IndicesDrawCount, GL_UNSIGNED_INT, nullptr);
正如我所提到的,当我不进行批处理时,绘图(它不会经历所有这些过程)可以正常工作,因此网格中的数据和顶点/索引缓冲区必须是好的,我认为它是设置索引缓冲区的方法是错误的,因为我什至不确定如何设置它(与其他渲染内容不同)。
结果是下一个(应该是实心球体):
“球体”的呈现方式让我认为索引是错误的。中心的对象是未经批处理绘制的对象,让我知道这不是初始设置错误。有人看到我做错了吗?
【问题讨论】:
您好,这取决于您的目标是什么。批处理可能是也可能不是适合这项工作的工具...... 嗨!你是什么意思?我实际上是在做批处理只是为了了解它是如何工作的,而不是为了优化或类似的东西,所以我真的很想实现它 【参考方案1】:我终于解决了(我哭了,我已经有很多时间了)。
所以有几个问题:
第一:我发布的函数s_3DData->IBuffer = IndexBuffer::Create(indices, s_3DData->MaxIndices);
正在做下一个:
glCreateBuffers(1, &m_BufferID);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferID);
glBufferData(GL_ARRAY_BUFFER, count * sizeof(uint), nullptr, GL_STATIC_DRAW);
所以第一个问题是我使用 GL_STATIC_DRAW
而不是 GL_DYNAMIC_DRAW
创建索引缓冲区,因为我们正在动态更新缓冲区(这是我不完全发布函数的坏处,我很漂亮我发的时候睡着了,我应该做的)。
第二个:函数glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, s_3DData->Indices.size(), s_3DData->Indices.data());
在size
参数上出错了。
OpenGL要求这个函数的大小是我们要更新的缓冲区的总大小,不是向量大小而是向量大小乘以sizeof(uint)
(在本例中为uint,因为向量是一个uint 向量)。
第三:最后一个问题是修改每个网格绘制上的索引向量的循环,这是错误的,从绘制二维四边形的角度来看是错误的(因为我之前正在测试批处理二维)。
正确的循环是下一个:
std::vector<uint> indices = mesh->m_Indices;
for (uint i = 0; i < indices.size(); ++i)
s_3DData->Indices.push_back(s_3DData->IndicesCurrentOffset + indices[i]);
++s_3DData->IndicesDrawCount;
++s_3DData->RendererStats.IndicesCount; // Debug Purpose
s_3DData->IndicesCurrentOffset += mesh->m_MaxIndex;
所以现在每个网格都存储了它所拥有的(max index + 1)
(对于索引从 0 到 3 的四边形,这将是 4)。
这样,我可以在更新我们用于绘制的索引时遍历所有网格索引,然后我可以更新current offset
值,以便我们按顺序正确存储所有绘制的索引。
再一次,我不打算让它变得快速或执行,我只是在学习如何做到这一点(我做到了:))。
结果:
【讨论】:
以上是关于批处理渲染器 (OpenGL) 的索引问题的主要内容,如果未能解决你的问题,请参考以下文章
OpenGL入门之渲染管线pipeline,着色器Shader