Athena: More Uses for Framebuffer Objects

Depth Buffer using Frame Buffer ObjectAs I was upgrading my engine to the latest version of OpenGL, I noticed how my depth buffer was broken again (I use it for debugging my scenes when on the debug menu), and despite many changes, I was still having trouble getting it to display properly.

The glReadPixels() function with GL_DEPTH_COMPONENT was working, but I was having no luck at getting it displaying at any colour other than white without converting it first before writing it to a texture with glDrawPixels() – on reflection a floating point texture might have made this easier.

Anyways, when looking for answers, I found a way of using frame buffer objects to write out to multiple colour buffers, which allowed me to modify my Blinn shader to render a scene and the depth buffer to texture at the same time.

First of all, the shader needs to be shaders, instead of gl_FragColor, we use gl_FragData[n], where n is the index of the colour buffer we want to write to.

//
// Varying Variables
varying float v_farPlane;
varying float v_nearPlane;
 
//
// Fragment Shader entry point
void main()
{
	// Write Pixel Colour
	//gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
	gl_FragData[0] = vec4( 1.0, 0.0, 0.0, 1.0 );
 
	// Write Depth
	float kDepth = 1.0 - ( gl_FragCoord.z / gl_FragCoord.w ) / ( v_farPlane - v_nearPlane );
	gl_FragData[1] = vec4( kDepth, kDepth, kDepth, 1.0 ); // Luminace style
}

 

And finally when binding your frame buffer object.

//
//
void RenderTarget::Setup()
{
	// Create Frame Buffer
	glGenBuffers( 1, &m_frameBuffer );
 
	// Bind Frame Buffer
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_frameBuffer );
 
	// Attach texture to receive colour
	glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_colorTexture, 0 );
 
	// Attach Texture to receive depth buffer
	glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, m_depthTexture, 0 );
 
	// Attach Depth and Stencil Buffer (same object since my gfx card doesn't support them being separate)
	glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_stencilDepthBuffer, 0 );
	glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, m_stencilDepthBuffer, 0 );
}
 
//
//
void RenderTarget::StartRender()
{
	// Bind Frame Buffer
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, m_frameBuffer );
 
	// Set which index draws to which colour attachment
	const GLenum kBuffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
	glDrawBuffers( 2, kBuffers );
}

 

The call to glDrawBuffers() sets where gl_FragData[x] is being wrote to, I’ve specified there are two indexes, the first one points to colour attachment 0, and the second points to colour attachment 1.

Now when an object is rendered using that shader, it will output the normal colour buffer to colour attachment 0, and output the depth buffer to colour attachment 1.

Also, don’t forget to reset the draw buffer destination after you’re finished with it.

//
//
void RenderTarget::EndRender()
{
	// Reset draw buffer destination
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
 
	// Unbind frame buffer and all attachments
	glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
}

 

I will probably remove the glBindFramebufferEXT() call to unbind the frame buffer at some point, so that the same frame buffer gets used for all render targets, only the attachments get changed.

I’ve read it is faster this way. For more information on this, there is this great article on GameDev.net about it called, OpenGL Frame Buffer Object 201.

I probably won’t use this for writing out the depth buffer, but it did give me some ideas for per pixel lighting.