FXAA Shader Anti-aliasing in XNA 4.0 Winforms

I had the challenge of trying to get some type of anti-aliasing in a project that uses the winforms method of XNA 4 that was being run on a laptop in REACH mode.

My first step was figuring out a method to use. I found quite a few shader based methods available, but I could get none of them to work. FXAA written by a engineer at NVIDIA seemed like the best option, so I went with that. I wrote up a question on Stack Overflow to see if anyone could help.

Eventually with enough research and …playing around… with the code I have it functioning. Here is an example to help anyone who might be interested in doing the same thing.


I’m using this example project’s XNA 4.0 form control in an application I’m writing: http://creators.xna.com/en-US/sample/winforms_series1

If you are unfamiliar with FXAA, check out the creator’s site: http://timothylottes.blogspot.com/2011/03/nvidia-fxaa.html

I’ve spent quite a bit of time so far trying to figure out to use it without any luck (so far…). It’s true I don’t have much experience at all with graphics programming, but I do currently have a nice application running, it just looks really really poor with the jagged lines. I know about the built-in method for AA, but doesn’t work for me and my laptop computer. So my request is about using FXAA and not the built-in methods.

At this point: I have FXAA 3.11 header file in my Content project. I have a generic FX file generated by visual studio with a few things like:

#define FXAA_HLSL_5 1
#include “Includes/Fxaa3_11.h”

I’m just asking here to see if anyone could provide some XNA 4.0 examples, specifically with using that custom windows forms method.

I appreciate any help someone might be able to provide.

Edit 3: I’ve been trying to work out how to get FXAA working since I posted this message. I found this: http://www.gamedev.net/topic/609638-fxaa-help/page_st_20 and this: http://fxaa-pp-inject.assembla.me/trunk/DirectX9/shader.fx

I striped down FXAA to bare bones FXAA_PC_CONSOLE type and it compiles. I just need to figure out the parameter fxaaConsolePosPos that is the top left and bottom right position of each pixel. Anyways, it looks like FXAA_PC_CONSOLE might work with shader model 2.0 that I need to use with REACH and winforms based XNA.


So I figured it out, at least using the lesser version of FXAA designed for consoles and low-end PCs. I can’t guarantee my parameters to the shader code are correct, but I do see a noticeable difference when it is running.

Here is the complete solution with my chopped up shader and pieces of the C# XNA 4.0 code:

The shader code first (put that in a .fx file in your Content sub-project): note that I replaced tex2Dlod with tex2D as per a suggestion that SM2.0 doesn’t support the first type

#define FxaaBool bool
#define FxaaDiscard clip(-1)
#define FxaaFloat float
#define FxaaFloat2 float2
#define FxaaFloat3 float3
#define FxaaFloat4 float4
#define FxaaHalf half
#define FxaaHalf2 half2
#define FxaaHalf3 half3
#define FxaaHalf4 half4
#define FxaaSat(x) saturate(x)
#define FxaaInt2 float2
#define FxaaTex sampler2D
#define FxaaTexTop(t, p) tex2D(t, float4(p, 0.0, 0.0))
#define FxaaTexOff(t, p, o, r) tex2D(t, float4(p + (o * r), 0, 0))

FxaaFloat FxaaLuma(FxaaFloat4 rgba) {
    rgba.w = dot(rgba.rgb, FxaaFloat3(0.299, 0.587, 0.114));
    return rgba.w;

                         FXAA3 CONSOLE - PC VERSION
FxaaFloat4 FxaaPixelShader(
    FxaaFloat2 pos,
    FxaaFloat4 fxaaConsolePosPos,
    FxaaTex tex,
    FxaaFloat4 fxaaConsoleRcpFrameOpt,
    FxaaFloat4 fxaaConsoleRcpFrameOpt2,
    FxaaFloat fxaaConsoleEdgeSharpness,
    FxaaFloat fxaaConsoleEdgeThreshold,
    FxaaFloat fxaaConsoleEdgeThresholdMin
) {
    FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy));
    FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw));
    FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy));
    FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw));
    FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy);

    #if (FXAA_GREEN_AS_LUMA == 0)
        FxaaFloat lumaM = rgbyM.w;
        FxaaFloat lumaM = rgbyM.y;

    FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw);
    lumaNe += 1.0 / 384.0;
    FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw);
    FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe);
    FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe);
    FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw);
    FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw);
    FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold;
    FxaaFloat lumaMinM = min(lumaMin, lumaM);
    FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled);
    FxaaFloat lumaMaxM = max(lumaMax, lumaM);
    FxaaFloat dirSwMinusNe = lumaSw - lumaNe;
    FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM;
    FxaaFloat dirSeMinusNw = lumaSe - lumaNw;

    if (lumaMaxSubMinM < lumaMaxScaledClamped)
        return rgbyM;

    FxaaFloat2 dir;
    dir.x = dirSwMinusNe + dirSeMinusNw;
    dir.y = dirSwMinusNe - dirSeMinusNw;
    FxaaFloat2 dir1 = normalize(dir.xy);

    FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw);
    FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw);

    FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness;
    FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0);

    FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw);
    FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw);

    FxaaFloat4 rgbyA = rgbyN1 + rgbyP1;
    FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25);

    #if (FXAA_GREEN_AS_LUMA == 0)
        FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax);
        FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax);

    if (twoTap)
        rgbyB.xyz = rgbyA.xyz * 0.5;

    return rgbyB;

uniform extern float SCREEN_WIDTH;
uniform extern float SCREEN_HEIGHT;
uniform extern texture gScreenTexture;

sampler screenSampler = sampler_state {
    Texture = <gScreenTexture>;
    /*MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;*/

float4 PixelShaderFunction(float2 tc : TEXCOORD0) : COLOR0 {
    float pixelWidth = (1 / SCREEN_WIDTH);
    float pixelHeight = (1 / SCREEN_HEIGHT);
    float2 pixelCenter = float2(tc.x - pixelWidth, tc.y - pixelHeight);
    float4 fxaaConsolePosPos = float4(tc.x, tc.y, tc.x + pixelWidth, tc.y + pixelHeight);

    return FxaaPixelShader(
        float4(-0.50 / SCREEN_WIDTH, -0.50 / SCREEN_HEIGHT, 0.50 / SCREEN_WIDTH, 0.50 / SCREEN_HEIGHT),
        float4(-2.0 / SCREEN_WIDTH, -2.0 / SCREEN_HEIGHT, 2.0 / SCREEN_WIDTH, 2.0 / SCREEN_HEIGHT),

technique ppfxaa {
    pass Pass1 {
        PixelShader = compile ps_2_0 PixelShaderFunction();

Here is a snippet of C-sharp code to apply the shader:

// These objects are used in managing the FXAA operation
// FXAA objects (anti-aliasing)
RenderTarget2D renderTarget;
SpriteBatch spriteBatch;
Effect fxaaAntialiasing;

// Initialize the render target and set effect parameters
// Code to handle final anti-aliasing using a pixel shader
renderTarget = new RenderTarget2D(

spriteBatch = new SpriteBatch(GraphicsDevice);
fxaaAntialiasing = content.Load<Effect>("sfxaa");
fxaaAntialiasing.CurrentTechnique = fxaaAntialiasing.Techniques["ppfxaa"];
fxaaAntialiasing.Parameters["gScreenTexture"].SetValue(renderTarget as Texture2D);

// This should happen in your Draw() method
// Change to our offscreen render target

// Draw all of your models and such here...
// ...


// This is where the shader anti-aliasing happens to the frame we just filled with contents
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend,
    SamplerState.LinearClamp, DepthStencilState.Default,
    RasterizerState.CullNone, fxaaAntialiasing);

// Draw the buffer we made to the screen
spriteBatch.Draw(renderTarget as Texture2D,
    new Rectangle(0, 0, renderTarget.Width, renderTarget.Height),