XNA Game Making

Resources for making games with XNA

10 Post-Processing Effects

Post-processing in shaders involves rendering a scene to a texture, applying a pixel (fragment) shader to it, and then rendering a rectangle that is the same size as the screen with the modified texture on it. Sound fun? Well there are plenty of ways to set up the post processing framework and we won't worry about it here. But once you are set up here are ten post-processing hlsl effects to experiment with.

The 10 Effects

Negative Effect - Single Color Effect - Black or White

Grayscale - Animated Ripple - Zoom - Edge Detection

Simple Blur - Gamma Correction - Checkerboard

Source Code - Download Here

Note the source code was written with illustrative purposes in mind and is not optomized.

Default View

Here is little 3D scene thrown together pretty quickly. It will be used a test/example for the post processing effects.

1) Negative Effect

Our first effect is super simple; it is really just one simple line:

color.rgb = 1 - color.rgb;

And that one line produces the "negative" image below. It's something that really just one line of code can make such a change in the image.

 

2) Single Color Effect

This effect converts the image to have hues of a single color. It finds the intensity of each pixel using the formula:

Intensity = 0.3R + 0.59G + 0.11B

And then multiplies the intensity by the single color and outputs it. The color in this example is (255, 180, 30). Here is the main code:

const float3 luminance = float3(0.3, 0.59, 0.11);

const float3 singleColor = float3(1, 0.7, 0.15);

float4 color = tex2D(screenTextureSampler, input.textureCoordinate);

float4 intensity = dot(color.rgb, luminance);

return float4(intensity * singleColor, 1);

 

3) Black or White

A simple black or white shader that averages the RGB color of each pixel and sets it black or white based off of predefined cutoff values:

const float minCutOff = 0.3;

const float maxCutOff = 0.85;

float4 color = tex2D(screenTextureSampler, input.textureCoordinate);

float colorAverage = (color.r + color.g + color.b) / 3.0;

if(colorAverage < minCutOff || colorAverage > maxCutOff)

color.rgb = 0;

else

color.rgb = 1;   

return color;

Changing the cutoff values can really alter the effect.

 

4) Grayscale

 Even simpler than black or white is using the dot product with a constant float3  to make a grayscale effect:

color.rgb = dot(color.rgb, float3(0.3, 0.59, 0.11));

 

5) Animated Ripple

This effect uses a timing variable set by the application and ripples the image using sine. It may not be as visible in the static picture but it has a fairly nice effect animated.

 

6) Zoom

This zoom effects let's you enter a screen coordinate to center the zoom around and a zoom factor. The actual zooming is a translation and multiply:

float2 texOffset = zoomSpotNorm + (input.textureCoordinate * zoomFactorInverse);

 

7) Edge Detection

An old classic. This edge detection works by finding the differences in intensity of a pixel neighbors above and below and left and right. these two differences are summed and scaled by a factor. (The intensities are found the same as in the single color effect. Here is the edge detection of neighbors:

float3 absoluteDifference = scale * ( abs(up - down) + abs(left - right) );

return float4(absoluteDifference, 1);

Here it is with a scale of 3:

 

8) Simple Blur

This simple blur samples the pixels around a given pixel and averages them. The efficient way to do this is in two passes, one for the horizontal direction and another for the vertical. The example here just makes a single pass and averages over a 5 x 5 area:

 

9) Gamma Correction

This applies the formula f(c) = c^(1/Y). Below is an image with gamma set to 3.5.

 

10) Checkerboard

This may not really count as a post-processing effect, but I like it. Just take the width and height of a square, divide the current texture coordinate by it, and if both are even or odd give one color, else give it another.