使用OpenGL / GLSL,统一缓冲区对象(UBO)无法正常工作

时间:2014-07-15 18:14:03

标签: opengl glsl fragment-shader vertex-shader

我目前正在开发一款小型3D引擎。在此之前,我曾经使用像glUniformXXX这样的方法将我的统一变量(逐个)发送到着色器程序。所以我不得不为每一帧发送它们(它没有得到很好的优化)。所以我决定使用Uniform Buffer Object在程序初始化时发送所有这些统一变量。因此,材料数据将通过状态变化使用VBO,IBO或PBO以相同的方式在缓冲区中加载一次。当然,如果我出于某种原因需要修改缓存中的数据,我总是可以使用像glMapBuffer和glUnmapBuffer这样的方法来更新数据。

对于我的例子,我加载了一个简单的ADS着色器程序(Ambient,Diffuse和Specular lighting)。现在为了简单起见,将Shininess组件(用于计算镜面反射光)放入片段着色器中的统一块中,如下所示(我们需要计算ADS着色的所有剩余均匀变量用作简单的统一变量):

[...]

layout (std140) uniform MaterialBlock
{
    float Shininess;
};

[...]

vec3 Specular = LightInfos[idx].Ls * MaterialInfos.Ks * pow(max(dot(reflectDir, viewDir), 0.0f), Shininess);

[...]

现在,我将向您展示加载我们的UBO的客户端代码:

void video::SpecularEffect::SetupMaterials(std::string const &name)
{
    GLuint blockIndex = 0, bindingPoint = 0, bufferIndex = 0;
    float shininess = 96.0f;
    {
        blockIndex = glGetUniformBlockIndex(this->m_Handle, "MaterialBlock");

        glUniformBlockBinding(this->m_Handle, blockIndex, bindingPoint);
        {
            glGenBuffers(1, &bufferIndex);
            glBindBuffer(GL_UNIFORM_BUFFER, bufferIndex);
            {
                glBufferData(GL_UNIFORM_BUFFER, sizeof(float), &shininess, GL_DYNAMIC_DRAW);
            }
            glBindBuffer(GL_UNIFORM_BUFFER, 0);
        }
        glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, bufferIndex);
  [...]
}

如您所见,上面的代码描述了以下原理图:

[*.frag]               BlockIndex     BindingPoint        *pBuffer

uniform MaterialBlock     [0] ----------> [1] <----------- [96.0f] //The shininess value = 96.0f
{
    float Shininess;
};

现在这里是渲染功能:

void video::RenderBatch::Render(void)
{
    type::EffectPtr pShaderEffect = EffectManager::GetSingleton()
        .FindEffectByName(this->m_pMaterial->GetAssocEffectName());

    pShaderEffect->Bind();
    {
        ///VERTEX ATTRIBUTES LOCATIONS.
        {
            pShaderEffect->BindAttribLocation(scene::VERTEX_POSITION, "VertexPosition");
            pShaderEffect->BindAttribLocation(scene::VERTEX_TEXTURE, "VertexTexture");
            pShaderEffect->BindAttribLocation(scene::VERTEX_NORMAL, "VertexNormal");
        }
        //SEND MATRIX UNIFORMS.
        {
            glm::mat3 normalMatrix = glm::mat3(glm::vec3(this->m_ModelViewMatrix[0]),
                glm::vec3(this->m_ModelViewMatrix[1]), glm::vec3(this->m_ModelViewMatrix[2]));

            pShaderEffect->SetUniform("ModelViewProjMatrix", this->m_ModelViewProjMatrix);
            pShaderEffect->SetUniform("ModelViewMatrix", this->m_ModelViewMatrix);
            pShaderEffect->SetUniform("NormalMatrix", normalMatrix);
        }
        this->SendLightUniforms(pShaderEffect);

        pShaderEffect->SetupMaterials(this->m_pMaterial->GetName());
        {
            this->m_pVertexArray->Lock(); //VAO binding
            {
                this->m_pIndexBuffer->Lock(); //IBO binding
                {
                    //Draw call here
                }
                this->m_pIndexBuffer->Unlock();
            }
            this->m_pVertexArray->Unlock();
        }
    }
    pShaderEffect->Release();
}

正如您所看到的,我称之为方法&#39; SetupMaterials&#39;直接在循环中。但这是不正确的,因为我每帧重新分配缓冲区,而且UBO没有像这样使用。目标是将材料数据加载到缓存中一次。但是渲染是正确的(因此统一块似乎已正确初始化,并且着色器程序可以访问缓冲区):

enter image description here

但是现在如果我只是在初始化时加载并填充缓冲区一次,那么在主循环之前,我有以下显示:

enter image description here

但是,我没有OpenGL错误(使用&#39; glGetError&#39;)。

另外,我尝试使用&#39; glBindBuffer(GL_UNIFORM_BUFFER,bufferIndex)&#39;来绑定UBO。在上面的渲染方法中,但我仍然有相同的显示!

那么,如果我直接在主循环中调用加载方法,为什么显示正确?如果我在初始化时调用它一次它为什么不起作用?我怎么能解决我的问题?

非常感谢您的帮助!

0 个答案:

没有答案