I've been playing around with rendering shadow maps using FBOs in OpenGL. I set up a simple directional light in my engine and managed to get a shadow map rendered. My Scene is a big castle and the shadow was cast over most of the other objects in the scene. Out of curiosity I removed the castle and just added a simple floor and my shadow map no longer renders properly. It seems that only one object at a time is making it through my render pass and onto the shadow map.
Here's an overview of the way the render is structured:
void RenderingManager::RenderGL()
{
PreRenderGL();
ShadowPassGL();
//Other user defined render passes here?
ScenePassGL();
//Cleanup
directionalLights.clear();
pointLights.clear();
renderables.clear();
}
The ShadowPassGL()
call is fairly simple
void RenderingManager::ShadowPassGL()
{
glViewport(0, 0, 1024, 1024);
for (unsigned int i = 0; i < directionalLights.size(); i++)
directionalLights[i]->RenderShadowMap(ShadowShader);
}
The RenderShadowMap(Shader* shadowShader)
call is a bit more complicated but it seems the orthographic view is setup properly, the look at view is also correct. I have verified that the matrices that are generated by my math functions work.
void DirectionalLightInternal::RenderShadowMapGL(Shader* shadowShader)
{
//Bind shadow shader for rendering shadow maps
shadowShader->bindShader();
//Send info about the directional light to the shader for shadow mapping
Vector3 focalPoint = direction * -10;
//Compute MVP from light's direction
Matrix4 depthProjection = Matrix4::getOrthographicProjection(-10, 10, -10, 10, -20, 20);
Matrix4 depthView = Matrix4::getLookAtView(focalPoint, Vector3(0, 0, 0), Vector3(0, 1, 0));
depthMVP = depthView * depthProjection;
shadowShader->setGlobalMatrix4("depthMVP", depthMVP);
//Actually bind framebuffer and render shadow map for texture
//Bind buffer for drawing
glBindFramebuffer(GL_FRAMEBUFFER, shadowBuffer);
glClearDepth(1.0f);
glClear(GL_DEPTH_BUFFER_BIT);
//render buffer
RenderingManager::RenderPass();
//Clean up
glBindFramebuffer(GL_FRAMEBUFFER, 0);
shadowShader->freeShader();
}
And the final bit of code that I think is relevant is the RenderPass()
method which just loops through all renderable objects and tries to render them with RenderObjectGL(Renderable renderable)
.
void RenderingManager::RenderObjectGL(Renderable renderable)
{
Mesh* mesh = renderable.mesh;
GLint shaderProgram;
glGetIntegerv(GL_CURRENT_PROGRAM, &shaderProgram);
GLuint vPosition = glGetAttribLocation(shaderProgram, "vPosition");
GLuint vNormal = glGetAttribLocation(shaderProgram, "vNormal");
GLuint vTexCoord = glGetAttribLocation(shaderProgram, "vTexCoord");
glEnableVertexAttribArray(vPosition);
glEnableVertexAttribArray(vNormal);
glEnableVertexAttribArray(vTexCoord);
int normalOffset = mesh->getPointSize();
int texCoordOffset = normalOffset + mesh->getNormalSize();
glBindBuffer(GL_ARRAY_BUFFER, mesh->getVBO());
glVertexAttribPointer(vPosition, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
glVertexAttribPointer(vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(normalOffset));
glVertexAttribPointer(vTexCoord, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(texCoordOffset));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->getIBO());
//Draw Shape
glDrawElements(GL_TRIANGLES, mesh->getNumberOfVerts(), GL_UNSIGNED_SHORT, (void *)0);
}
//Render every object in the scene; assume a bound shader
void RenderingManager::RenderPassGL()
{
for(unsigned int i = 0; i < renderables.size(); i++)
RenderObjectGL(renderables[i]);
}
I figured some images to illustrate the problem would be pretty useful.
When I render with the castle it looks like this:
And the shadow map from the scene is:
Interestingly the shadow map is really just of the castle, the other objects aren't in the shadow map.
Now the scene with just the floor:
And its shadow map:
Which again seems to be of just the floor, none of the other objects in my scene. Is there some aspect of FBOs that I'm missing here?