我似乎正在使用glMultiDraw间接(MDI)和/或绘制元素间接命令(DEIC)不正确,因为我无法正确渲染所有对象。该方法尝试对相似的对象和纹理重用绘制命令(“实例化”...ish),在所有位置绘制所有对象。 “调试”方法仅对每个对象(两个三角形)使用 1 个绘制命令,但输出的对象始终太少,第一个对象的位置从未用于任何对象。
This is what happens when using the failed attempt at instancing:
This is what happens when using the 'debug' method of one object (two triangles) per DEIC:
目标是正确使用 DEIC 中的 instanceCount 来允许接近实例化的东西,同时在正确的位置绘制正确数量的对象。我在 google-raid 中的经历表明 DEIC 的 baseInstance 字段could如果 DEIC 存储在缓冲区中,则可用作drawID。如果这是不可能的,或者如果我对用途有很大误解,请打电话告诉我!我尝试包含最少量的适用代码,以避免发布 10,000 字的帖子。
下面,我创建“绘制路径”对象,它们是要加载到缓冲区中的缓冲区 ID 和向量的集合(基于与此问题无关的许多变量)。
// VAO
glGenVertexArrays(1, &p->vertexArrayObject);
glBindVertexArray(p->vertexArrayObject);
// vertices
glCreateBuffers(1, &p->vertexBuffer);
glBindVertexBuffer(bindingIndex, p->vertexBuffer, 0, sizeof(Vertex));
glEnableVertexArrayAttrib(p->vertexArrayObject, 0);
glEnableVertexArrayAttrib(p->vertexArrayObject, 1);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, offsetof(Vertex, position));
glVertexAttribBinding(0, bindingIndex);
glVertexAttribFormat(1, 2, GL_FLOAT, GL_TRUE, offsetof(Vertex, uv));
glVertexAttribBinding(1, bindingIndex);
if(p->pathType == DrawPathType::FAST)
{
glNamedBufferStorage(p->vertexBuffer, p->rbVertices.bufferSize, nullptr, m_persistentCreateFlags);
p->rbVertices.ptr = (Vertex*)glMapNamedBufferRange(p->vertexBuffer, 0, p->rbVertices.bufferSize, m_persistentMapFlags);
p->rbVertices.bufferFragment = p->rbVertices.bufferSize / 3;
}
// indices
glCreateBuffers(1, &p->indexBuffer);
glVertexArrayElementBuffer(p->vertexArrayObject, p->indexBuffer);
// draw commands
// glCreateBuffers(1, &p->drawCmdBuffer);
// glBindBuffer(GL_DRAW_INDIRECT_BUFFER, p->drawCmdBuffer);
// glNamedBufferStorage(p->drawCmdBuffer, p->rbCommands.bufferSize, nullptr, m_persistentCreateFlags);
// p->rbCommands.ptr = (DEIC*)glMapNamedBufferRange(p->drawCmdBuffer, 0, p->rbCommands.bufferSize, m_persistentMapFlags);
// p->rbCommands.bufferFragment = p->rbCommands.bufferSize / 3;
// unsure how this works
// glEnableVertexArrayAttrib(p->vertexArrayObject, 2);
// glVertexAttribIFormat(2, 1, GL_UNSIGNED_INT, offsetof(DrawElementsIndirectCommand, baseInstance));
// glVertexAttribBinding(2, bindingIndex);
// glVertexBindingDivisor(bindingIndex, 1);
// draw IDs
glCreateBuffers(1, &p->drawIDBuffer);
glBindBuffer(GL_ARRAY_BUFFER, p->drawIDBuffer);
glEnableVertexAttribArray(2);
glVertexAttribIPointer(2, 1, GL_UNSIGNED_INT, sizeof(GLuint), 0);
glVertexAttribDivisor(2, 1);
// transforms
glCreateBuffers(1, &p->transformBuffer);
if(p->pathType == DrawPathType::LONG || p->pathType == DrawPathType::FAST)
{
glNamedBufferStorage(p->transformBuffer, p->rbTransforms.bufferSize, nullptr, m_persistentCreateFlags);
p->rbTransforms.ptr = (glm::mat4*)glMapNamedBufferRange(p->transformBuffer, 0, p->rbTransforms.bufferSize, m_persistentMapFlags);
p->rbTransforms.bufferFragment = p->rbTransforms.bufferSize / 3;
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, p->transformBuffer);
}
// texture addresses
glCreateBuffers(1, &p->textureAddressBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, p->textureAddressBuffer);
这是“renderPrep”函数的有用部分。
for(size_t i = 0; i < glyphs->size(); i++)
{
auto it = glyphs->at(i);
// ensure we have a valid texture address
if(!it->textureAddress.defined())
{
Logger::getInstance().Log(Logs::CRIT, Logs::Drawing, "Renderer2D::drawPrep()", "Iteration [{}] of [{}] has a null texture address (0,0)!", i, glyphs->size());
failed++;
}
else
{
offset = verts->size();
for(int in = 0; in < QUAD_VERTS; in++) { indices->push_back(baseQuadIndices[in] + offset); }
// creating our model space to world space matrix ('model' in "projection * view * model")
glm::mat4 transRotate = glm::rotate(identMat, glm::radians(it->angle), glm::vec3(0.0f, 0.0f, 1.0f));
transforms->push_back(transRotate);
transforms->back() = glm::translate(transforms->back(), it->position);
// push back modelspace vertices
for(auto& v : it->vertices) { verts->push_back(v); }
// update previous draw command or create a new one
if(currentTex.exists() && currentTex == it->textureAddress)
{
// append previous draw command
DEICs->back().vertexCount += QUAD_VERTS;
DEICs->back().instanceCount += 1; // two triangles, thus two instances
}
else
{
// different texture, new draw command
DEIC tmp = { QUAD_VERTS, 1, (inc * QUAD_VERTS), (inc * BASE_VERTS), inc };
DEICs->push_back(tmp);
currentTex = it->textureAddress;
}
/// \NOTE: Current issue is that the draw command is only drawing one object, in two iterations.
/// This is responsible for the blank second box
/* DEIC tmp = { QUAD_VERTS, 1, (inc * QUAD_VERTS), (inc * BASE_VERTS), 0 };
DEICs->push_back(tmp);
texAddrs->push_back(it->textureAddress); */
Logger::getInstance().Log(Logs::DEBUG, Logs::Drawing, "Renderer2D::drawPrep()",
"\n\033[93mDEIC #{}\033[0m:\n\tvertCount\t\t{}\n\tinstCount\t\t{}\n\tfirstInd\t\t{}\n\tbaseVert\t\t{}\n\tbaseInst\t\t{}\n",
DEICs->size(), DEICs->back().vertexCount, DEICs->back().instanceCount, DEICs->back().firstIndex, DEICs->back().baseVertex, DEICs->back().baseInstance);
texAddrs->push_back(currentTex);
p->drawIDs.push_back(inc);
inc++;
}
}
这是实际负责渲染的代码片段。
int activeProgramID = 0; // currently used glsl program
glGetIntegerv(GL_CURRENT_PROGRAM, &activeProgramID);
// active passed glsl program id, or enable existing if not already enabled
if(glProgID > 0) { glUseProgram(glProgID); }
else if(activeProgramID == 0) { glUseProgram(m_prog->getProgramID()); }
// all clear, do it!
glBindVertexArray(p->vertexArrayObject);
// bind SSBOs, if applicable
if(p->transformBuffer) { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, p->transformBuffer); }
if(p->textureAddressBuffer) { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, p->textureAddressBuffer); }
// finally render
//if(p->drawCmdBuffer) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, p->drawCmdBuffer); glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0, p->drawCommands.size(), 0); }
//else { glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, p->drawCommands.data(), p->drawCommands.size(), 0); }
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, p->drawCommands.data(), p->drawCommands.size(), 0);
// update ring buffer(s), if applicable
if(p->rbCommands.ptr != nullptr) { m_bufferLockManager->lockRange(p->rbCommands.oldHead, p->rbCommands.bufferFragment); }
if(p->rbTransforms.ptr != nullptr) { m_bufferLockManager->lockRange(p->rbTransforms.oldHead, p->rbTransforms.bufferFragment); }
if(p->rbVertices.ptr != nullptr) { m_bufferLockManager->lockRange(p->rbVertices.oldHead, p->rbVertices.bufferFragment); }
// options specific to a "fast" draw path (if a fast draw path, glyphs are single use)
if(p->pathType == DrawPathType::FAST) { p->clear(true); }
// clean up
glBindVertexArray(0);
// change to previous glProgram
if(activeProgramID) { glUseProgram(activeProgramID); }
else { glUseProgram(0); }
编辑 #1, 2019-04-05 11:53a 美国东部时间:
首先,我忘记了着色器!抱歉错过了这一点。
// --------- Vertex shader ------------
// uniforms / shader_storage_buffer object
layout(std140, binding = 0) buffer CB0 { mat4 Transforms[]; };
// view & projection in one
uniform mat4 ViewProjection;
// input
layout(location = 0) in vec3 In_v3Pos;
layout(location = 1) in vec2 In_v2TexCoord;
layout(location = 2) in uint In_uiDrawID;
// output
out DrawBlock
{
vec2 v2TexCoord;
flat uint iDrawID;
} Out;
void main()
{
mat4 World = Transforms[In_uiDrawID + gl_InstanceID];
vec4 worldPos = World * vec4(In_v3Pos, 1.0);
gl_Position = ViewProjection * worldPos;
Out.v2TexCoord = In_v2TexCoord;
Out.iDrawID = In_uiDrawID;
}
// --------- Fragment shader ------------
struct TexAddress
{
sampler2DArray arr;
float slice;
};
layout (std430, binding = 1) buffer CB1 { TexAddress texAddress[]; };
// input
in DrawBlock
{
vec2 v2TexCoord;
flat uint iDrawID;
} In;
// output
layout(location = 0) out vec4 Out_v4Color;
vec4 Texture(TexAddress addr, vec2 uv) { return texture(addr.arr, vec3(uv, addr.slice)); }
void main()
{
int DrawID = int(In.iDrawID);
Out_v4Color = vec4(Texture(texAddress[DrawID], In.v2TexCoord).xyz, 1.0f);
}
如果我使用非 DSA 删除了 drawIDs 块并替换为下面的代码片段,它会绘制聚焦于屏幕中心的白色三角形。
glCreateBuffers(1, &p->drawCmdBuffer);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, p->drawCmdBuffer);
glNamedBufferStorage(p->drawCmdBuffer, p->rbCommands.bufferSize, nullptr, m_persistentCreateFlags);
p->rbCommands.ptr = (DEIC*)glMapNamedBufferRange(p->drawCmdBuffer, 0, p->rbCommands.bufferSize, m_persistentMapFlags);
p->rbCommands.bufferFragment = p->rbCommands.bufferSize / 3;
glEnableVertexArrayAttrib(p->vertexArrayObject, 2);
glVertexAttribIFormat(2, 1, GL_UNSIGNED_INT, offsetof(DrawElementsIndirectCommand, baseInstance));
glVertexAttribBinding(2, bindingIndex);
glVertexBindingDivisor(2, 1);
Outcome:
编辑 #2 @ 2019-04-06 12:09p 美国东部时间:在 github 上创建了一个要点,其中包含渲染器的完整标头/源代码。关联 :https://gist.github.com/bbilyeu/bbf74ef4eaf979b5d2b4f2c2a9dcce48 https://gist.github.com/bbilyeu/bbf74ef4eaf979b5d2b4f2c2a9dcce48