我目前正在开发一款小型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没有像这样使用。目标是将材料数据加载到缓存中一次。但是渲染是正确的(因此统一块似乎已正确初始化,并且着色器程序可以访问缓冲区):
但是现在如果我只是在初始化时加载并填充缓冲区一次,那么在主循环之前,我有以下显示:
但是,我没有OpenGL错误(使用&#39; glGetError&#39;)。
另外,我尝试使用&#39; glBindBuffer(GL_UNIFORM_BUFFER,bufferIndex)&#39;来绑定UBO。在上面的渲染方法中,但我仍然有相同的显示!
那么,如果我直接在主循环中调用加载方法,为什么显示正确?如果我在初始化时调用它一次它为什么不起作用?我怎么能解决我的问题?
非常感谢您的帮助!