In this page you will implement post-fx in OpenGL. We will see how to use Framebuffer Objects to apply successive manipulations to an image using a ping pong mechanism on textures attached to a framebuffer.


First steps

Start from this page to get the base code, or start from the code you wrote if you followed another page.

Build a scene looking like that.


In this tutorial you will need to render your lighted scene in a texture, we will call this image the beauty pass. You will also need to render a depth pass.

If you use forward shading, you will need to create a framebuffer object and attach two textures to it, on color texture and one depth texture.

If you use deferred shading, you very likely have a depth pass ready, from you gbuffer. You simply need to modify your code to render your lighting pass into a framebuffer on which you will only need to attach a color texture.

To create and fill a framebuffer object, you can refer to the corresponding part in the shadow mapping page.

Start by creating a framebuffer dedicated to postfx. This fbo will only have one draw buffer.

Then create four window-sized textures.

Attach the first texture to the framebuffer.

As always check if your framebuffer is complete.

In the next code snippets, the texture storing the illumination will be called BEAUTY and the texture storing depth will be called DEPTH.

Add a blit viewport to visualize you beauty pass, if you do not have one already.

Gamma correction

In this part you will apply a simple color correction to your image.

Create a new fragment shader containing the following code. You can reuse the blit vertex shader from the lighting page.

In the main loop after lighting computation but before the debug display and UI, draw a quad inside the default framebuffer using the shader you just created.

Start by resetting the default framebuffer. Always aim to reduce OpenGL state changing calls by only modifying needed states.

Draw the quad with the gamma shader. Verify your results.

Modify your shader to apply the following forma to the input texture : color^\frac{1}{\gamma} with 0 < \gamma < 6, pass gamma as a uniform and make it editable by the UI.



This postfx will alway be the last of the processing chain, you will need to change the input texture each time you add another processing block in the chain.

Sobel Operator

The Sobel operator is a basic edge detection filter. You will find detailed explanation on the wikipedia page.

Start by creating a new fragment shader.

Then you need to render a full screen quad, before the gamma filter and inside the postfx framebuffer.

Start by using the framebuffer you created before, keep it activated until gamma correction.

Attach to it the texture in which you wish to render the SOBEL pass, it will be one of the textures from the fxTexture array.

Clear the contents of the texture you attached to the framebuffer.

Draw your quad using the sobel shader, and pass it the BEAUTY texture. Verify that you still have the same result and that your gamma correction is still active.

You need now to implement the Sobel operator in your shader, start by adding the two kernels used by the operator to the fragment shader.

You now need to apply the convolution to each pixel using the pixel neighborhood. Read the documentation of the texelFetch function.

Create a 3×3 matrix storing the norm of the current pixel and its neighbors.

Compute the convolution of the two kernels with this matrix.

Display the result.


Modify the code to mix the input texture with the operator result according to a factor editable with the UI.


Basic Depth of Field

The depth of field post processing simulates rendering through a lens. You will implement a basic version of this technique with four passes and three different shaders between the sobel operator and the gamma correction. Be careful to identify properly the texture you read using glBindTexture and the texture being written using glFramebufferTexture2D.


To blur an image, you replace the value of a pixel by the mean value of the pixel and its neighbors.  A blur is separable filter, it means that you can apply the filter horizontally and then vertically in another pass and still get the same result, it is much more efficient to use a separable filter on a 2D image, especially for bug kernels.

Create a new shader using the following code.

Filter your image two consecutive times using this shader.  You need to alternate the textures bind and attached so that the second filter use the texture processed by the first filter.

Check that your result does not change and that you still can tweak the sobel factor and the gamma correction.

Modify your shader to implement the blur operator. Add the two following uniforms to your shader.

Compute the mean using a 1D kernel in the direction of the uniform.

Vary the direction between the two shader calls and verify your results by varying kernel size.


Circle of Confusion

Compute for each pixel the blur coefficient also called circle of confusion.

Create a new shader using the following code.

Apply this shader on a quad and bind the DEPTH texture.

Verify the result by passing the COC texture to your debug overlay viewports.

Modify the shader in order to compute the blur coefficient according to the depth. Add the following uniforms.

Convert the depth to camera space.

L’uniform Focus est un vec3 contenant successivement les trois plans de near, focus, et far.

The Focus uniform is a vec3, it contains the three planes, near, focus, and far.

Use the following code to implement it.


Depth of Field

To implement the final depth of field pass, you only need to mix the SOBEL texture and the horizontal blur texture using the value stored in the COC texture.

Create yet again a new fragment shader.

Use the shader on a quad and pass it the SOBEL, HBLUR, and COC textures.

Modify the shader to implement a mix between the SOBEL and HBLUR texture using the value stored in the COC texture as a coefficient.