Hello everybody,
I'm trying to use the depth component of a frame buffer as a texture in my vertex shader. However, when I try to use these float values I get the wrong results, even though the texture I see in the debugger has the correct values.
The scene looks as follows:
First of all, here I create the framebuffer and depth component:
// Initialise the texture to be used for shadow mapping.
glGenTextures(1, &texture_id_);
glBindTexture(GL_TEXTURE_2D, texture_id_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, depth_texture_size_, depth_texture_size_, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Set up depth comparison mode.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
// Set up wrapping mode.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Set the texture id for the depth texture.
glActiveTexture(GL_TEXTURE0 + light_->getDepthTextureId());
glBindTexture(GL_TEXTURE_2D, texture_id_);
// Create FBO to render depth into.
glGenFramebuffers(1, &fbo_id_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
// Attach the depth texture to it.
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_id_, 0);
// Disable colour rendering.
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
// Unbind.
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glActiveTexture(GL_TEXTURE0 + 30);
Here is how I render using this framebuffer:
void ShadowRenderer::render(const FrustumCaster& cam)
{
active_entities_.clear();
// Gather all the leaf nodes.
//scene_manager_->visitLeafs(*this);
unsigned int pre_rendered_objects_ = 0;
glCullFace(cull_mode_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
glViewport(0, 0, depth_texture_size_, depth_texture_size_);
glClearDepth(1.0f);
glClear(GL_DEPTH_BUFFER_BIT);
glm::vec3 camera_location = cam.getLocation();
glm::mat4 view_matrix = cam.getViewMatrix();
glm::mat4 perspective_matrix = cam.getPerspectiveMatrix();
CreateAnimatedShadowMapShader& animated_shader = CreateAnimatedShadowMapShader::getShader();
ShadowShader& shader = ShadowShader::getShader();
frustum_->setFrustum(perspective_matrix * view_matrix);
// New rendering method.
Region* region = scene_manager_->getRoot().findRegion(cam.getLocation());
if (region != NULL)
{
std::stringstream ss;
std::vector<const Portal*> processed_portals;
region->preRender(*frustum_, cam.getLocation(), *this, false, pre_rendered_objects_, 0, processed_portals, ss);
for (std::vector<SceneNode*>::const_iterator ci = scene_manager_->getPlayers().begin(); ci != scene_manager_->getPlayers().end(); ++ci)
{
(*ci)->preRender(*frustum_, cam.getLocation(), *this, false, pre_rendered_objects_);
}
}
// If we cannot find a region we fall back on the true and tested... Although this
// is more a debuf feature and should be removed in future versions of this rendering
// engine.
else
{
SceneNode& root = scene_manager_->getRoot();
root.preRender(*frustum_, cam.getLocation(), *this, false, pre_rendered_objects_);
}
for (std::vector<const RenderableSceneLeaf*>::const_iterator ci = active_entities_.begin(); ci != active_entities_.end(); ++ci)
{
const RenderableSceneLeaf* leaf = *ci;
if (leaf->getShadowType() == ShadowRenderer::NO_SHADOW || !leaf->isInFrustum(*frustum_))
{
continue;
}
if (leaf->isDoubleSided())
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
}
switch (leaf->getShadowType())
{
case ShadowRenderer::ANIMATED_SHADOW:
leaf->draw(view_matrix, perspective_matrix, active_lights_, &animated_shader);
break;
case ShadowRenderer::STATIC_SHADOW:
leaf->draw(view_matrix, perspective_matrix, active_lights_, &shader);
break;
}
}
// Unbind.
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// Reset viewport.
int orgWidth, orgHeight;
glfwGetWindowSize(&orgWidth, &orgHeight);
glViewport(0, 0, orgWidth, orgHeight);
}
This all works fine. When I read out the values in the depth attachment:
std::vector<float> m_depth_;
m_depth_.resize(light.getShadowMapDimension() * light.getShadowMapDimension());
glBindFramebuffer(GL_FRAMEBUFFER, light.getShadowRenderer().getFramebufferId());
m_depth_.resize(light.getShadowMapDimension() * light.getShadowMapDimension());
glReadPixels(0, 0, light.getShadowMapDimension(), light.getShadowMapDimension(), GL_DEPTH_COMPONENT, GL_FLOAT, &m_depth_[0]);
It returns the right values and all read 5.19999, which is the value I expected. When I look at the debugger I see the following:
This value is as expected. Now, when I render the scenes and I try to use these depth values it all falls apart...
Here is the vertex shader:
#version 420
uniform sampler2D depth_texture;
uniform mat4 projection_matrix;
uniform mat4 modelview_matrix;
uniform mat4 model_matrix;
uniform mat4 light_shadow_matrix;
uniform float z_near;
uniform float z_far;
in vec3 a_Vertex;
in float a_cos;
void main(void)
{
// TODO Make these values in uniforms and calculate them on the CPU.
float a = z_far / (z_far - z_near);
float b = z_far * z_near / (z_near - z_far);
vec4 light_pos = light_shadow_matrix * model_matrix * vec4(a_Vertex, 1.0);
vec4 depth_value = texture2DProj(depth_texture, light_pos);
float actual_depth = b / (depth_value.r - a);
actual_depth /= a_cos;
// Change the location of this vertex by multiplying it with the depth value.
vec4 pos = modelview_matrix * vec4((normalize(a_Vertex) * actual_depth), 1.0);
gl_Position = projection_matrix * pos;
}
Fragment shader:
#version 420
out vec4 outColor;
void main(void)
{
outColor = vec4(1.0, 0.0, 0.0, 1.0);
}
I initialise these shaders as follows:
void ShadowVolumeShader::initialise(const SceneLeafModel& model_node, const glm::mat4& view_matrix, const glm::mat4& model_matrix, const glm::mat4& projection_matrix, const std::vector<const SceneLeafLight*>& lights)
{
if (lights.empty())
{
std::cout << "Could not initialise shader as there are no lights!" << std::endl;
return;
}
Light& light = lights[0]->getLight();
if (depth_not_initialised_)
{
loadDepth(light);
glGenBuffers(1, &m_cos_buffer_);
glBindBuffer(GL_ARRAY_BUFFER, m_cos_buffer_);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_cos_.size(), &m_cos_[0], GL_STATIC_DRAW);
depth_not_initialised_ = false;
}
bindShader();
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
glDisableVertexAttribArray(6);
glDisableVertexAttribArray(7);
glm::mat4 model_view_matrix = view_matrix * model_matrix;
//Send the modelview and projection matrices to the shaders
glUniformMatrix4fv(model_matrix_loc_, 1, false, glm::value_ptr(model_matrix));
glUniformMatrix4fv(modelview_matrix_loc_, 1, false, glm::value_ptr(model_view_matrix));
glUniformMatrix4fv(projection_matrix_loc_, 1, false, glm::value_ptr(projection_matrix));
glUniformMatrix4fv(light_shadow_matrix_loc_, 1, false, glm::value_ptr(light.getShadowMatrix()));
glActiveTexture(GL_TEXTURE0 + light.getDepthTextureId());
glBindTexture(GL_TEXTURE_2D, light.getTextureId());
glUniform1i(depth_texture_loc_, light.getDepthTextureId());
glUniform1f(z_near_loc_, 0.1f);
glUniform1f(z_far_loc_, 60.0f);
//Bind the vertex array and set the vertex pointer to point at it
glBindBuffer(GL_ARRAY_BUFFER, model_node.getModel().getVertexBufferId());
glVertexAttribPointer((GLint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_cos_buffer_);
glVertexAttribPointer((GLint)1, 1, GL_FLOAT, GL_FALSE, 0, 0);
}
When I use these depth values to render the volume that makes up the light using this setup I get this:
The values down the diagonal are correct, but the others get rendered to the far plane. Now If I use the depth values I get from reading the depth bufferr using glReadPixels and feed it as a vertex attribute I get the correct values and the volume renders correctly:
This result is with the updated vertex shader:
#version 420
uniform sampler2D depth_texture;
uniform mat4 projection_matrix;
uniform mat4 modelview_matrix;
uniform mat4 model_matrix;
uniform mat4 light_shadow_matrix;
uniform float z_near;
uniform float z_far;
in vec3 a_Vertex;
in float a_Depth;
in float a_cos;
out vec2 texCoord0;
void main(void)
{
// TODO Make these values in uniforms and calculate them on the CPU.
float a = z_far / (z_far - z_near);
float b = z_far * z_near / (z_near - z_far);
float actual_depth = b / (a_Depth - a);
actual_depth /= a_cos;
// Change the location of this vertex by multiplying it with the depth value.
vec4 pos = modelview_matrix * vec4((normalize(a_Vertex) * actual_depth), 1.0);
texCoord0 = a_TexCoord0;
gl_Position = projection_matrix * pos;
}
Now, what do I do wrong that gives me the wrong depth values?
Any help is appreciated, thanks!
Bram