Reputation: 23
For the last couple of days I've been working on doing some lighting with a forward rendering system. So far, so good, but when I move the mesh I'm rendering away from the global center (0, 0, 0) or rotate it, all the lights (except for ambient light) start "flickering" (but ONLY on that mesh which is away from (0, 0, 0)). When I have a second mesh which' position is the global center, the lights act normally on that one).
It may be worth mentioning that I am only able to run in release mode since running in debug mode gives me a strange exception. I believe that has something to do with the SFML libaries...
This is what it looks like when i illuminate the object with directional light (notice that the mesh with the grey brick texture doesn't show this strange effect):
https://www.dropbox.com/s/yt3ozxyeg8e6psa/Screenshot%20%284%29.png?dl=0
And with spot light:
https://www.dropbox.com/s/1yk2j0yg4fca6pz/Screenshot%20%285%29.png?dl=0
I use GLEW 1.10.0 for loading OpenGL functions and SFML 2.1 for window stuff. My IDE is Visual Studio 2013 Ultimate and I'm running on Windows 8.1 with the latest Nvidia graphics drivers.
Here is the code that does the blending:
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);
}
And here are a couple of other important methods:
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);
}
Upvotes: 1
Views: 186
Reputation: 834
This is sometimes the case but the real answer to this problem is in Edit2 section.
I've noticed an effect like that a while ago in my application. I believe it has something to do with number precision on the GPU. The further from 0,0,0 you are, the greater this error is. One solution to this problem is to scale down the world. If for eg. 1 meter in your world is value 1.0 you could scale it down to 0.1. That's however not a real solution, just a workaround.
There's one more thing. I remember that in my case this bug was present only when running the application via Visual Studio. Try running it from .exe and see if the problem persists. Since it was not present in the release version of my game I just ignored it and assumed that it must had something to do with the debug mode of the graphics card.
Edit:
Ok, one more thing comes to my mind. Why are you using GL_EQUAL instead of GL_LEQUAL? I know that you think that since you're drawing the same model twice, it's position should be the same but are you absolutely sure about that? There's no good reason to prefer GL_EQUAL to GL_LEQUAL in this case. Try changing it and see if it helps. Moving away from 0,0,0 would explain it since 0,0,0 is the only place where no transformations are applied to the model. Every transformation can cause imprecisions which may be the reason why your Z-test fails.
Edit2:
This suggestion may make things better but it won't fix them totally. It's just a guess but let me tell you what's happening in gameObject->Render function. You PUSH a matrix, draw and then POP the matrix, right? Now you think that you have exactly the same transformation now? You don't! It's almost the same. To make things work you need to re-design your program. Don't PUSH/POP any matrices between drawing the same object and you should be good using GL_EQUAL comparison.
Edit3:
My final advice is: make double sure that the transformation matrix you're using is each pass is eaxactly the same. If you can't find a reason why it could be different use this simple hack-around. Use a small offset value that you pass to shader as a uniform. Increase this value slightly in each pass (experiment what is should be. I think something really small will do the trich). Then when you transform each vertex by yor transform and view-projection, subtract this offset from your Z coordinate. This way each light will be drawn a little bit before the previous one.
Edit4: Another issue (which happened to be the case here) is GLSL and C++ may perform matrix multiplication slightly different so performing the same operation in shader and in C++ may produce different effects thus leading to artifacts.
Upvotes: 1