在过去的几天里,我一直致力于使用前向渲染系统进行一些照明。到目前为止,还不错,但是当我将要渲染的网格从全局中心 (0, 0, 0) 移开或旋转它时,所有灯光(环境光除外)开始“闪烁”(但仅限于此)远离 (0, 0, 0)) 的网格。当我有第二个网格时,它的位置是全局中心,灯光通常作用在那个网格上)。
可能值得一提的是,我只能在 Release模式下运行,因为在 Debug模式下运行会给我一个奇怪的异常。我相信这与 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,我在装有最新 Nvidia 图形驱动程序的 Windows 8.1 上运行。
这是进行混合的代码:
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);
}
最佳答案
有时会出现这种情况,但这个问题的真正答案在 Edit2 部分。
我不久前在我的应用程序中注意到了类似的效果。我相信这与 GPU 上的数字精度有关。你离 0,0,0 越远,这个错误就越大。解决这个问题的一种方法是缩小世界。如果例如。在你的世界里,1 米的值(value)是 1.0,你可以将它缩小到 0.1。然而,这不是真正的解决方案,只是一种解决方法。
还有一件事。我记得在我的案例中,这个错误仅在通过 Visual Studio 运行应用程序时出现。尝试从 .exe 运行它,看看问题是否仍然存在。由于它不存在于我的游戏的发行版本中,我只是忽略了它并假设它一定与显卡的 Debug模式有关。
编辑:
好吧,我又想到一件事。你为什么使用 GL_EQUAL 而不是 GL_LEQUAL?我知道您认为既然您两次绘制同一个模型,它的位置应该是相同的,但您对此有绝对的把握吗?在这种情况下,没有充分的理由更喜欢 GL_EQUAL 而不是 GL_LEQUAL。尝试更改它,看看是否有帮助。远离 0,0,0 可以解释它,因为 0,0,0 是唯一没有对模型应用任何转换的地方。每个转换都可能导致不精确,这可能是 Z 检验失败的原因。
编辑2:
这个建议可能会让事情变得更好,但不会完全解决问题。这只是一个猜测,但让我告诉您 gameObject->Render 函数中发生了什么。你 PUSH 一个矩阵,绘制然后 POP 矩阵,对吧?现在你认为你现在有完全相同的转变?你不知道!几乎一样。要使事情正常进行,您需要重新设计您的程序。不要在绘制同一对象之间推送/弹出任何矩阵,你应该善于使用 GL_EQUAL 比较。
编辑3:
我的最终建议是:再次确保您使用的变换矩阵在每个过程中都完全相同。如果您找不到它可能不同的原因,请使用这个简单的 hack-around。使用作为统一传递给着色器的小偏移值。在每次通过时稍微增加这个值(试验应该是什么。我认为一些非常小的东西会产生问题)。然后,当您通过变换和 View 投影变换每个顶点时,从 Z 坐标中减去此偏移量。这样一来,每盏灯都会先于前一盏灯绘制。
编辑4: 另一个问题(恰好是这里的情况)是 GLSL 和 C++ 执行矩阵乘法可能略有不同,因此在着色器和 C++ 中执行相同的操作可能会产生不同的效果,从而导致伪影。
关于c++ - 混合光的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25547853/