Search code examples
xnaantialiasing

Examples for using Nvidia FXAA (shader antialiasing) in XNA 4.0 winforms?


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_PC_CONSOLE 1
#define FXAA_HLSL_5 1
#define FXAA_QUALITY__PRESET 12
#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.


Solution

  • 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;
        #else
            FxaaFloat lumaM = rgbyM.y;
        #endif
        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);
        #else
            FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax);
        #endif
        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(
            pixelCenter,
            fxaaConsolePosPos,
            screenSampler,
            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),
            8.0,
            0.125,
            0.05);
    }
    
    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 a final antialiasing using a pixel shader
    renderTarget = new RenderTarget2D(
        GraphicsDevice,
        GraphicsDevice.PresentationParameters.BackBufferWidth,
        GraphicsDevice.PresentationParameters.BackBufferHeight,
        false,
        GraphicsDevice.PresentationParameters.BackBufferFormat,
        DepthFormat.Depth24);
    
    spriteBatch = new SpriteBatch(GraphicsDevice);
    fxaaAntialiasing = content.Load<Effect>("sfxaa");
    fxaaAntialiasing.CurrentTechnique = fxaaAntialiasing.Techniques["ppfxaa"];
    fxaaAntialiasing.Parameters["SCREEN_WIDTH"].SetValue(renderTarget.Width);
    fxaaAntialiasing.Parameters["SCREEN_HEIGHT"].SetValue(renderTarget.Height);
    fxaaAntialiasing.Parameters["gScreenTexture"].SetValue(renderTarget as Texture2D);
    
    
    //..........................................
    //this should happen in your Draw() method
    
    //change to our offscreen render target
    GraphicsDevice.SetRenderTarget(renderTarget);
    GraphicsDevice.Clear(Color.CornflowerBlue);
    
    //
    //draw all of your models and such here...
    //
    
    GraphicsDevice.SetRenderTarget(null);
    GraphicsDevice.Clear(Color.Black);
    
    //this where the shader antialiasing happens to the frame we just filled with content
    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), 
        Color.White);
    
    spriteBatch.End();