How to read the float values of a depth component in the vertex shader?

Started by
-1 comments, last by Morloth 9 years, 4 months ago

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:

lB5P73G.png

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:

Dlxzo0F.png

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:

97LVm3q.png

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:

GU2GYkN.png

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

This topic is closed to new replies.

Advertisement