glDrawArrays之间的glBufferSubData调用mangling数据

时间:2013-11-11 01:24:06

标签: c++ opengl

似乎glBufferSubData覆盖或以某种方式在我的glDrawArrays来电之间修改数据。我在Windows 7 64bit工作,使用最新的Nvidia GeForce GT520M CUDA 1GB驱动程序。

我有2个模型,每个模型都有一个动画。模型有1个网格,该网格存储在同一个VAO中。它们每个也有1个动画,用于渲染网格的骨骼变换存储在同一个VBO中。

我的工作流程如下:

  • 计算模型的骨骼转换矩阵
  • 使用glBufferSubData将骨骼转换矩阵加载到opengl中,然后绑定缓冲区
  • 使用glDrawArrays
  • 渲染模型网格

对于一个模型,这是有效的(至少,大多数情况下 - 有时我会在顶点之间产生奇怪的间隙)。

但是,对于多个模型,看起来骨骼变换矩阵数据在渲染调用网格之间变得混杂。

Single Model Animated Windows
Two Models Animated Windows

我加载了我的骨骼转换数据:

void Animation::bind()
{
    glBindBuffer(GL_UNIFORM_BUFFER, bufferId_);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, currentTransforms_.size() * sizeof(glm::mat4), &currentTransforms_[0]);
    bindPoint_ = openGlDevice_->bindBuffer( bufferId_ );
}

我像这样渲染我的网格:

void Mesh::render()
{
    glBindVertexArray(vaoId_);
    glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
    glBindVertexArray(0);
}

如果我在致电glFinish()后向render()添加了一个电话,那就可以了!这似乎向我表明,由于某种原因,一个动画的变换矩阵数据是“流血”。到下一个动画。

怎么会发生这种情况?我的印象是,如果我在使用该缓冲区时调用glBufferSubData(例如,对于glDrawArrays),则会阻塞。情况不是这样吗?

值得一提的是,这个相同的代码在Linux中运行良好。

注意:与我删除的previous post相关。

网格加载代码:

void Mesh::load()
{
    LOG_DEBUG( "loading mesh '" + name_ +"' into video memory." );

    // create our vao
    glGenVertexArrays(1, &vaoId_);
    glBindVertexArray(vaoId_);

    // create our vbos
    glGenBuffers(5, &vboIds_[0]);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[0]);
    glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(glm::vec3), &vertices_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[1]);
    glBufferData(GL_ARRAY_BUFFER, textureCoordinates_.size() * sizeof(glm::vec2), &textureCoordinates_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[2]);
    glBufferData(GL_ARRAY_BUFFER, normals_.size() * sizeof(glm::vec3), &normals_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[3]);
    glBufferData(GL_ARRAY_BUFFER, colors_.size() * sizeof(glm::vec4), &colors_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(3);
    glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 0, 0);

    if (bones_.size() == 0)
    {
        bones_.resize( vertices_.size() );
        for (auto& b : bones_)
        {
            b.weights = glm::vec4(0.25f);
        }
    }

    glBindBuffer(GL_ARRAY_BUFFER, vboIds_[4]);
    glBufferData(GL_ARRAY_BUFFER, bones_.size() * sizeof(VertexBoneData), &bones_[0], GL_STATIC_DRAW);
    glEnableVertexAttribArray(4);
    glVertexAttribIPointer(4, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
    glEnableVertexAttribArray(5);
    glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)(sizeof(glm::ivec4)));

    glBindVertexArray(0);
}

动画UBO设置:

void Animation::setupAnimationUbo()
{
    bufferId_ = openGlDevice_->createBufferObject(GL_UNIFORM_BUFFER, Constants::MAX_NUMBER_OF_BONES_PER_MESH * sizeof(glm::mat4), &currentTransforms_[0]);
}

其中Constants::MAX_NUMBER_OF_BONES_PER_MESH设置为100。

OpenGlDevice

GLuint OpenGlDevice::createBufferObject(GLenum target, glmd::uint32 totalSize, const void* dataPointer)
{
    GLuint bufferId = 0;
    glGenBuffers(1, &bufferId);
    glBindBuffer(target, bufferId);

    glBufferData(target, totalSize, dataPointer, GL_DYNAMIC_DRAW);
    glBindBuffer(target, 0);

    bufferIds_.push_back(bufferId);

    return bufferId;
}

1 个答案:

答案 0 :(得分:1)

这些使用标记对于这种情况大多是正确的,但您可以考虑尝试GL_STREAM_DRAW

由于某些原因,您的驱动程序似乎无法隐式同步,因此您可能希望尝试一种首先消除同步需求的技术。我建议 Buffer Orphaning :在发送数据之前,为数据指针调用glBufferData (...)为NULL。这将允许当前使用UBO的命令继续使用原始数据存储而不强制同步,因为您将在发送新数据之前分配新的数据存储。当前面提到的命令完成时,原始数据存储将被孤立,GL实现将释放它。

在较新的OpenGL实现中,您可以使用glInvalidateBuffer[Sub]Data (...)来暗示驱动程序执行上面讨论的操作。同样,您可以使用glMapBufferRange (...)和适当的标志来更明确地控制所有这些行为。取消映射将隐式刷新并同步对缓冲区对象的访问,除非另有说明,如果您不想使用无同步缓冲区更新逻辑,这可能会让您的驱动程序完成其工作。

我提到的大部分内容都会更详细地讨论here