批处理渲染器 (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-&gt;IBuffer = IndexBuffer::Create(indices, s_3DData-&gt;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-&gt;Indices.size(), s_3DData-&gt;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 在延迟渲染器中实现天空盒

OpenGL入门之渲染管线pipeline,着色器Shader

使用 OpenGL / LibGDX 的后处理问题 - 黑屏(FBO、着色器、四边形)

OpenGL 对象不显示,着色器相关

OpenGL:渲染到 FBO 时是不是支持纹理组合器功能?

无法从 OpenGL 中的着色器访问先前渲染的纹理