Postprocessing effects on WP7, Part I

One of the things I miss most when programming for Windows Phone 7 are shaders. As you can probably see in the other samples of this site, I am quite fond of them, so they were greatly missed in all my time working with XNA on WP7.

All this is about to change soon, as Windows Phone 8 will support DirectX and shaders. But of course, as a developer, you can’t forget WP7 users, so ideally you would want to have at least some effects also available in the WP7 version of your game. So I thought I’d share some of the tricks we used to make postprocessing effects on WP7 in Chickens Can’t Fly. Of course, not all of the effects you’ll see here actually made it into the game. Some were just experiments that were ultimately left out of the game, but maybe you’ll find use for them in your own games.

In this part, I’ll show two effects that use Color Blending to obtain some color manipulations.

Color Blending

In case you haven’t worked with color blending before, feel free to read the following articles:

Still here? Good šŸ™‚

The main takeaway from these articles should be this formula:

FinalColor = (SourceColor * SourceBlend) + (DestinationColor * DestinationBlend)

The meaning of all fields here is:

  • FinalColor – the final color that will appear on the screen
  • SourceColor – the color of the pixel being drawn
  • DestinationColor – the color of the pixel currently in the render target
  • SourceBlend and DestinationBlend – parameters we can tweak to obtain the effects we want

Going through all the possible values for these fields is out of scope of this post, and is covered in the above articles. Let’s now get to something more interesting.

Code Structure

In the accompanying code, all post-processing effects will have their own class, inheriting from:

public class PostprocessingEffect
{
    protected GraphicsDevice graphicsDevice;
    protected SpriteBatch spriteBatch;

    public PostprocessingEffect(GraphicsDevice graphicsDevice, SpriteBatch spriteBatch)
    {
        this.graphicsDevice = graphicsDevice;
        this.spriteBatch = spriteBatch;
    }

    public virtual Texture2D Apply(Texture2D input, GameTime gameTime)
    {
        // No effect here, just return the input
        return input;
    }
}

Setup code will go into the constructor, and the actual code to apply the effect will be places in the aptly named Apply function. To apply the effect, you’ll first need to render your game into a RenderTarget, and then pass that render target to the post processing effect you want to use. The code could be something similar to:

RenderTarget2D sceneContents;

protected override void LoadContent()
{
    [...] //other code goes here
    // The render target on which the game is drawn
    sceneContents = new RenderTarget2D(GraphicsDevice, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
}
///
<summary> /// This is where you draw your game
/// </summary>
private void DrawScene()
{
    [...] //draw the current scene of the game here
}

protected override void Draw(GameTime gameTime)
{
    // draw the game into a render target
    GraphicsDevice.SetRenderTarget(sceneContents);
    GraphicsDevice.Clear(Color.Black);
    DrawScene(); // draw the scene
    // return to rendering into the BackBuffer
    GraphicsDevice.SetRenderTarget(null);

    Texture2D result;
    // Apply the post processing effect
    result = somePostProcessingEffect.Apply(sceneContents, gameTime);

    // Draw the result on the screen
    GraphicsDevice.SetRenderTarget(null);
    GraphicsDevice.Clear(Color.Black);
    spriteBatch.Begin();
    spriteBatch.Draw(result, Vector2.Zero, Color.White);
    spriteBatch.End();
    base.Draw(gameTime);
}

Now we have all the basics covered, let’s see how the effects are implemented. To avoid adding code to draw a complex scene, we’ll just draw a screenshot taken from a nice game in the DrawScene method:

Effect: Negative Image

The purpose here is to achieve the negative of an image. In HLSL this is easily achieved by subtracting the original color from (1,1,1).

To simulate this using Color Blending, we’ll do:

  • Draw White into the Render Target
  • Set up a blend state with SourceBlend and DestinationBlend set to One, and the Blend Function set to ReverseSubtract

The final color will be: Final Color = -(SourceColor * One) + (DestinationColor * One)

And thus, you achieved a negative image.

That’s all, quite a basic effect really.

Effect: Color Boost

I’m not sure if there is a standard name for the next effect. We were looking for an effect that would brighten light areas of the image, and leave the rest unchanged. The result was an effect that looks quite interesting in situations like: just before an explosion, lightning flashes, radioactive effects, etc.

I’m sure the same effect can be achieved in a number of different ways, but this is how I discovered it, and I liked how it looked.

When setting up Color Blending, you can chose a user-defined color to be used for SourceBlend and DestinationBlend. This is specified through the BlendFactor. To achieve the ‘color boost’ effect, as we called it, we did the following:

  • set SourceBlend to BlendFactor
  • set DestinationBlend to SourceColor
  • set BlendFactor = boostColor * factor, where boostColor is the color we want to boost, and factor is the amount we want to boost it by
  • set the BlendFunction to Add

The final color will be:

    FinalColor = (SourceColor * BlendFactor + DestinationColor * SourceColor)

Which is actually

    FinalColor = SourceColor * (DestinationColor + BlendFactor)

Looking at the final formula, we can see that we could have simply done this by drawing the source image two times, once with Multiplicative blending, and the second time with Additive blending, tinted with the color we used for the BlendFactor, but this way we only draw it once, keeping our fillrate a bit lower.

Using White for our boost Color results in the effect seen below.

You could obtain a similar result by drawing a white transparent rectangle over the whole screen, which would increase the brightness of the image, but the ‘color boost’ effects preserves the darker parts of the image, leading to a more interesting contrast.

You can also try different colors for the boostColor parameter, for example blue or gold, and obtain the below effects.

The code

You can download the code here: Part1.zip

Running the sample and tapping the screen will take you through each effect. There are lots of effects you could create by playing around with ColorBlending, so I encourage you to experiment! Next time, we’ll take a look at adding random noise to your scene.

  • http://amapplease.blogspot.com Pete

    Hey, look who’s back posting articles … nice one!

    > “Iā€™m not sure if there is a standard name for the next effect.”

    I think “color boost” is fine. Maybe over-exposure or saturation would also fit, but I prefer color boost.

  • MikeBMcL

    I’d probably call it over-saturation or possibly bizarro-bloom. šŸ™‚ But color boost or super tint works fine, too. Shaders on Windows Phone will definitely be nice! Can’t wait to see all the exciting uses people make of them.

  • Pingback: Catalin's Blog » Postprocessing effects on WP7, Part II

  • Pingback: Catalin's Blog » Postprocessing effects on WP7, Part III