我似乎错误地使用了 glMultiDrawIndirect (MDI)和/或 DrawElementsIndirectCommand (DEIC),因为我无法正确渲染所有对象。尝试对相似的对象和纹理重复使用绘制命令的方法(“实例化” ... ish)会在所有位置绘制所有对象。 “调试”方法仅对每个对象(两个三角形)使用1个绘制命令,但输出始终是对象太少,第一个对象的位置永远不会用于任何对象。
在每个DEIC中使用一个对象(两个三角形)的“调试”方法时,会发生这种情况:
目标是正确使用DEIC中的instanceCount,以允许实例化,同时在正确的位置绘制正确数量的对象。我在Google突袭中的冒险经历表明,如果DEIC存储在缓冲区中,则DEIC 的baseInstance字段可以用作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 EST :
首先,我忘记了着色器!很抱歉错过这个机会。
// --------- 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);
编辑#2 @ 2019-04-06 12:09p EST :在github上创建了一个gist,其中包含渲染器的完整标题/源。链接:https://gist.github.com/bbilyeu/bbf74ef4eaf979b5d2b4f2c2a9dcce48
答案 0 :(得分:1)
元素,实例和绘制命令的数量都不同,并且引用的事物也不同。特别是,使用任何多绘制命令并不一定意味着您要进行任何实例化。
以下代码使用一个glMultiDrawArraysIndirect
调用发出两个绘图命令:
struct Vert {
vec2f position;
};
struct Inst {
box2f position;
vec4f color;
};
static const Vert verts[] = {
// quad
{ { 0, 0 } },
{ { 1, 0 } },
{ { 0, 1 } },
{ { 1, 1 } },
// triangle
{ { 0, 0 } },
{ { 1, 0 } },
{ { 0.5, 1 } },
};
static const Inst insts[] = {
// three quads
{ { -0.8, -0.8, -0.6, -0.6 }, { 1, 0, 0, 1 } },
{ { -0.1, -0.8, 0.1, -0.6 }, { 0, 1, 0, 1 } },
{ { 0.6, -0.8, 0.8, -0.6 }, { 0, 0, 1, 1 } },
// five triangles
{ { -0.8, 0.6, -0.6, 0.8 }, { 1, 1, 0, 1 } },
{ { -0.4, 0.6, -0.2, 0.8 }, { 0.1, 0.1, 0.1, 1 } },
{ { -0.1, 0.6, 0.1, 0.8 }, { 0, 1, 1, 1 } },
{ { 0.2, 0.6, 0.4, 0.8 }, { 0.1, 0.1, 0.1, 1 } },
{ { 0.6, 0.6, 0.8, 0.8 }, { 1, 0, 1, 1 } },
};
static const DrawArraysIndirectCommandSN cmds[] = {
// quads: 4 vertices, 3 instances, first vertex=0, first instance=0
{ 4, 3, 0, 0 },
// triangles: 3 vertices, 5 instances, first vertex=4, first instance=3
{ 3, 5, 4, 3 },
};
GLuint buf[3]; // vertex, instance, draw-command buffers
glCreateBuffers(3, buf);
glNamedBufferStorage(buf[0], sizeof(verts), verts, 0);
glNamedBufferStorage(buf[1], sizeof(insts), insts, 0);
glNamedBufferStorage(buf[2], sizeof(cmds), cmds, 0);
GLuint va;
glCreateVertexArrays(1, &va);
glVertexArrayVertexBuffer(va, 0, buf[0], 0, sizeof(Vert));
glVertexArrayVertexBuffer(va, 1, buf[1], 0, sizeof(Inst));
glVertexArrayBindingDivisor(va, 1, 1);
glVertexArrayAttribBinding(va, 0, 0);
glEnableVertexArrayAttrib(va, 0);
glVertexArrayAttribFormat(va, 0, 2, GL_FLOAT, 0, offsetof(Vert, position));
glVertexArrayAttribBinding(va, 1, 1);
glEnableVertexArrayAttrib(va, 1);
glVertexArrayAttribFormat(va, 1, 4, GL_FLOAT, 0, offsetof(Inst, position));
glVertexArrayAttribBinding(va, 2, 1);
glEnableVertexArrayAttrib(va, 2);
glVertexArrayAttribFormat(va, 2, 4, GL_FLOAT, 0, offsetof(Inst, color));
glClear(GL_COLOR_BUFFER_BIT);
glBindProgramPipeline(pp.get());
glBindVertexArray(va);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buf[2]);
glMultiDrawArraysIndirect(GL_TRIANGLE_STRIP, 0, sizeof(cmds)/sizeof(cmds[0]), 0);
#version 450 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec4 inst_position;
layout(location = 2) in vec4 color;
out gl_PerVertex {
vec4 gl_Position;
};
layout(location = 0) out PerVertex {
vec4 color;
} OUT;
void main() {
OUT.color = color;
gl_Position = vec4(mix(inst_position.xy, inst_position.zw, position), 0, 1);
}
#version 450 core
layout(location = 0) in PerVertex {
vec4 color;
} IN;
layout(location = 0) out vec4 OUT;
void main() {
OUT = IN.color;
}