#c #opengl
#c #opengl
Вопрос:
Похоже, я неправильно использую glMultiDrawIndirect (MDI) и / или DrawElementsIndirectCommand (DEIC), поскольку я не могу правильно отобразить все объекты. Метод, пытающийся повторно использовать команды рисования для похожих объектов и текстур (‘instancing’ … иш), рисует весь объект во всех местоположениях. Метод ‘debug’ просто использует 1 команду рисования для каждого объекта (два треугольника), но на выходе всегда получается слишком мало объектов, причем местоположение первого объекта никогда не используется ни для одного объекта.
Это то, что происходит при неудачной попытке создания экземпляра:
Это то, что происходит при использовании метода ‘debug’ для одного объекта (двух треугольников) на DEIC:
Цель состояла бы в том, чтобы правильно использовать instanceCount в DEIC, чтобы разрешить что-то, приближающееся к созданию экземпляра, при рисовании правильного количества объектов в правильных местах. Мои приключения в Google-raiding показали, что поле baseInstance DEIC может использоваться в качестве идентификатора drawID, если DEIC хранятся в буфере. Если это невозможно или я совершенно неправильно понимаю использование, пожалуйста, позвоните мне и дайте мне знать! Я попытался включить наименьшее количество применимого кода, чтобы избежать публикации с количеством 10 000 слов.
Ниже я создаю объекты «draw path», которые представляют собой набор идентификаторов буферов и векторов для загрузки в буферы (на основе многочисленных переменных, не связанных с этим вопросом).
// VAO
glGenVertexArrays(1, amp;p->vertexArrayObject);
glBindVertexArray(p->vertexArrayObject);
// vertices
glCreateBuffers(1, amp;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, amp;p->indexBuffer);
glVertexArrayElementBuffer(p->vertexArrayObject, p->indexBuffer);
// draw commands
// glCreateBuffers(1, amp;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, amp;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, amp;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, amp;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(autoamp; v : it->vertices) { verts->push_back(v); }
// update previous draw command or create a new one
if(currentTex.exists() amp;amp; 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()",
"n33[93mDEIC #{}33[0m:ntvertCounttt{}ntinstCounttt{}ntfirstIndtt{}ntbaseVerttt{}ntbaseInsttt{}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 ;
}
}
This is the snippet actually responsible for rendering.
int activeProgramID = 0; // currently used glsl program
glGetIntegerv(GL_CURRENT_PROGRAM, amp;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:53 по восточному времени:
Во-первых, я забыл шейдеры! Извиняюсь, что пропустил это.
// --------- Vertex shader ------------
// uniforms / shader_storage_buffer object
layout(std140, binding = 0) buffer CB0 { mat4 Transforms[]; };
// view amp; 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);
}
Если я удалил блок drawIDs с использованием не-DSA и заменил приведенным ниже фрагментом, он рисует белые треугольники с фокусом в центре экрана.
glCreateBuffers(1, amp;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:09 по восточному времени: Создал суть на github с полным заголовком / исходным кодом для средства визуализации. Ссылка :https://gist.github.com/bbilyeu/bbf74ef4eaf979b5d2b4f2c2a9dcce48
Комментарии:
1. Ваш код, похоже, действительно запутался в том, что он хочет сделать. Это наполовину стиль DSA, наполовину отдельный формат атрибута и наполовину vertexAttribPointer. Вам нужно остановиться на одном стиле настройки материала и придерживаться его. Действительно трудно сказать, где могут быть ошибки, когда ваш код, кажется, находится в состоянии войны с самим собой.
2. Формат атрибута в основном я лениво отлаживал, но вы делаете правильный вывод. Я буду помнить об этом в будущих вопросах.
Ответ №1:
Количество элементов, экземпляров и команд рисования различны и относятся к разным вещам. В частности, использование любой команды множественного рисования не обязательно означает, что вы выполняете какой-либо экземпляр.
Следующий код использует один glMultiDrawArraysIndirect
вызов для выдачи двух команд рисования:
- Визуализируйте три экземпляра объекта quad
- Визуализируйте пять экземпляров объекта «треугольник»
Данные
struct Vert {
float position[2];
};
struct Inst {
float position[4]; // as two corners
float color[4];
};
static const Vert verts[] = {
// quad
{ { 0, 0 } }, // vertex 0
{ { 1, 0 } },
{ { 0, 1 } },
{ { 1, 1 } },
// triangle
{ { 0, 0 } }, // vertex 4
{ { 1, 0 } },
{ { 0.5, 1 } },
};
static const Inst insts[] = {
// three quads
{ { -0.8, -0.8, -0.6, -0.6 }, { 1, 0, 0, 1 } }, // instance 0
{ { -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 } }, // instance 3
{ { -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, amp;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;
}
Результат:
Комментарии:
1. Я обновил свой пример вверху, чтобы использовать baseInstance gl_InstanceID для захвата преобразования. Частично это работает в том смысле, что первые две вершины кажутся в правильном месте… но это все, что правильно. Такое ощущение, что я упускаю из виду или чрезмерно усложняю что-то невероятно простое.
2. Трудно понять ваш код. Чего вы пытаетесь достичь с помощью
In_uiDrawID gl_InstanceID
? Почему вы смешиваете многократную отрисовку с созданием экземпляра? Почему вы используете,DrawElements
гдеDrawArrays
достаточно? Если вы хотите нарисовать кучу текстурированных прямоугольников, вы можете использовать простой экземпляр без какого-либо многократного рисования. Также для этого вам не нужны общие преобразования 4×4. Передаютvec4
s через атрибут VAO. Вам также не нужен буфер шейдеров для текстур. Также отправляйте свои текстуры без привязки в качестве атрибута VAO. Просто начните с опубликованного мной кода, который работает, полностью его поймите, а затем играйте с ним.