Chaining Post Effects

I’ve been working on a deferred rendering system for my game but I’m having trouble with some of the effects. Ideally the user would be able to enable/disable some of them (SSAO, FXAA, etc.) but I’ve been stuck for the best way to achieve this.

My current code runs a pixel shader on each stage using a sprite batch:

_effect.CurrentTechnique = _effect.Techniques["Shadows"];
GraphicsDevice.SetRenderTarget(_renderTarget);

SpriteBatch.Begin(effect: _effect, samplerState: _samplerState, rasterizerState: _rasterizerState);
SpriteBatch.Draw(_dataRenderImage, new Rectangle(0, 0, width, height), Color.White);
SpriteBatch.End();

_renderImage = (Texture2D)_renderTarget;

if (FXAA)
{
    _effect.CurrentTechnique = _effect.Techniques["FXAA"];

    SpriteBatch.Begin(effect: _effect, samplerState: _samplerState, rasterizerState: _rasterizerState);
    SpriteBatch.Draw(_renderImage, new Rectangle(0, 0, width, height), Color.White);
    SpriteBatch.End();

    _renderImage = (Texture2D)_renderTarget;
}

GraphicsDevice.SetRenderTarget(null);
_effect.CurrentTechnique = _effect.Techniques["Final"];

SpriteBatch.Begin(effect: _effect, samplerState: _samplerState, rasterizerState: _rasterizerState);
SpriteBatch.Draw(_renderImage, new Rectangle(0, 0, width, height), Color.White);
SpriteBatch.End();

This works when FXAA is disabled:

But applying FXAA produces a black screen

(Yes, the screenshot was necessary, shut up)

I’m guessing that this is because I am trying to render to the same buffer that I’m reading from which probably isn’t a great thing to do. If it’s not, and I’m doing something else wrong, disregard below.

I have a few potential fixes in mind, but none work particularly well:

  1. Create a new buffer for every pass. This would take up far more VRAM than I need and it would be a hassle to select the last buffer for my ‘final’ pass anyway
  2. Create a set of techniques for each option and select the one that fits the options. This grows exponentially and is pretty repetitive so I can’t see this being the standard
  3. create ‘dud’ passes where the shader just passes on the value in the pixel if the option hasn’t been enabled. Although this runs less code than number 1, I feel like there’s probably a way to not run the pass at all if it isn’t needed

What is normally done?

The VRAM is most likely not an issue on non-ancient hardware. And selecting the final buffer is easy with some abstraction, since it’s just a RenderTarget2D that’s passed on. Alternatively you could have just two RenderTargets and render back and forth between those for all the effects.