混合灯的麻烦

时间:2014-08-28 11:36:46

标签: c++ opengl 3d lighting blending

在过去的几天里,我一直在用前向渲染系统做一些照明。到目前为止,这么好,但是当我移动网格时,我将渲染远离全局中心(0,0,0)或旋转它,所有灯光(环境光线除外)开始"闪烁&# 34; (但仅限于远离(0,0,0)的网格)。当我有第二个网格'位置是全球中心,灯光通常在那个位置上运行。)

值得一提的是,我只能在发布模式下运行,因为在调试模式下运行会给我一个奇怪的例外。我认为这与SFML库有关...

当我用定向光照亮物体时,这就是它的样子(注意带有灰砖纹理的网格没有显示出这种奇怪的效果):
https://www.dropbox.com/s/yt3ozxyeg8e6psa/Screenshot%20%284%29.png?dl=0

用聚光灯:
https://www.dropbox.com/s/1yk2j0yg4fca6pz/Screenshot%20%285%29.png?dl=0

我使用GLEW 1.10.0加载OpenGL函数,使用SFML 2.1加载窗口函数。我的IDE是Visual Studio 2013 Ultimate,我在Windows 8.1上使用最新的Nvidia图形驱动程序运行。


以下是进行混合的代码:

void RenderingEngine::Render(GameObject* gameObject)
{
    Clear();
    m_baseLights.clear();
    gameObject->AddToRenderingEngine(this);
    gameObject->Render(this, *m_ambientShader);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    glDepthMask(GL_FALSE);
    glDepthFunc(GL_EQUAL);

    for (uInt i = 0; i < m_baseLights.size(); i++)
        gameObject->Render(this, *m_baseLights[i]->GetShader());

    glDepthFunc(GL_LESS);
    glDepthMask(GL_TRUE);
    glDisable(GL_BLEND);
}


以下是其他几个重要的方法:

void GameObject::Render(RenderingEngine* renderingEngine, const Shader& shader)
{
    for (uInt i = 0; i < m_children.size(); i++)
        m_children[i]->Render(renderingEngine, shader);

    for (uInt i = 0; i < m_components.size(); i++)
        // NOTE: This is where MeshRenderer::Render(...) etc. get called
        m_components[i]->Render(renderingEngine, shader);
}



// NOTE: MeshRenderer inherits GameComponent
void MeshRenderer::Render(RenderingEngine* renderingEngine, const Shader& shader)
{
    shader.Bind();
    shader.UpdateUniforms(*renderingEngine, m_material, GetParentObject().GetTransform());
    m_mesh.Draw();
}



void Shader::Bind() const
{
    glUseProgram(m_program);
}



// NOTE: SpotShader is one of many classes that inherits Shader and implements its pure virtual method UpdateUniforms(...)
void SpotShader::UpdateUniforms(const RenderingEngine& renderingEngine, const Material& material, const Transform& transform) const
{
    material.GetTexture()->Bind(0);

    SetUniformMat4f("viewProjection", renderingEngine.GetMainCamera().GetViewProjection());
    SetUniformMat4f("transform", transform.GetTransform());
    SetUniformSpotLight("spotLight", *m_spotLight);
    SetUniformf("specularIntensity", material.GetSpecularIntensity());
    SetUniformf("specularExponent", material.GetSpecularExponent());
    SetUniformVec3f("cameraPosition", renderingEngine.GetMainCamera().GetParentObject().GetTransform().GetTranslation());
}



void Mesh::Draw() const
{
    glBindVertexArray(m_vertexArrayObject);
    glDrawElements(GL_TRIANGLES, m_drawCount, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

1 个答案:

答案 0 :(得分:1)

有时情况如此,但这个问题的真正答案是在Edit2部分。

我在应用程序中注意到了一段时间的影响。我相信它与GPU上的数字精度有关。从0,0,0越远,此错误越大。这个问题的一个解决方案是缩小世界范围。如果是例如。世界上1米的值是1.0,你可以将它缩小到0.1。然而,这不是一个真正的解决方案,只是一种解决方法。

还有一件事。我记得在我的情况下,只有在通过Visual Studio运行应用程序时才会出现此错误。尝试从.exe运行它,看看问题是否仍然存在。由于它没有出现在我的游戏的发布版本中,我只是忽略了它,并认为它必须与显卡的调试模式有关。


编辑:

好的,还有一件事在我脑海里浮现。为什么使用GL_EQUAL而不是GL_LEQUAL?我知道你认为既然你曾经两次画过同一个模特,那么它的位置应该是一样的,但你是否完全确定?在这种情况下,没有理由更喜欢GL_EQUAL到GL_LEQUAL。尝试更改它,看看它是否有帮助。远离0,0,0将解释它,因为0,0,0是唯一没有转换应用于模型的地方。每次转换都可能导致不精确,这可能是您的Z测试失败的原因。

EDIT2:

这个建议可能会让事情变得更好,但它不会完全解决它们。这只是一个猜测,但让我告诉你gameObject-&gt;渲染功能发生了什么。你按下矩阵,绘制然后POP矩阵,对吗?现在你认为你现在有完全相同的转变?你没有!它几乎一样。为了使事情有效,您需要重新设计您的程序。不要在绘制相同对象之间推送/弹出任何矩阵,使用GL_EQUAL比较你应该很好。

EDIT3:

我的最后建议是:确保您使用的转换矩阵每次通过都是相同的。如果你找不到它可能有所不同的原因,请使用这个简单的黑客攻击。使用您作为制服传递到着色器的小偏移值。在每次传球中略微增加这个值(试验应该是什么。我认为一些非常小的东西会做这个传球)。然后,当您通过yor变换和视图投影变换每个顶点时,从Z坐标中减去此偏移量。这样每个灯光都会在前一个灯光之前稍微绘制一下。

Edit4: 另一个问题(这里恰好是这种情况)是GLSL和C ++可能会执行稍微不同的矩阵乘法,因此在着色器和C ++中执行相同的操作可能会产生不同的效果,从而导致伪像。