Skip to main content

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_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.

Forums for the win!

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;
    #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 final anti-aliasing 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 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),
    Color.White);

spriteBatch.End();


Popular posts from this blog

ChatGPT is a new, and faster, way to do programming!

Currently ChatGPT is in a free “initial research preview” . One of its well known use cases at this point is generating software code. I’ve also just used it to write most of this article… Well, actually a future article about cleaning up SRT subtitle files of their metadata faster than I have been by hand with Notepad++ and its replace functionality. Update: I recorded a screencast of writing the SRT subtitle cleaner application loading and processing portion. I relied heavily on ChatGPT for code. It was a fun process! https://youtu.be/TkEW39OloUA ChatGPT, developed by OpenAI, is a powerful language model that can assist developers in a variety of tasks, including natural language processing and text generation. One such task that ChatGPT can help with is creating an SRT cleaner program. SRT, or SubRip Subtitle, files are commonly used to add subtitles to video files. However, these files can become cluttered with unnecessary information, such as timing lines or blank spaces. To clean...

Theme error in 2010s Android App after AppCompat Migration

I plan on releasing a lot of my old work as GPL open source, but most of it has aged to the point that it no longer functions, or if it does work it’s running in compatibility mode. Basically it’s no longer best practices. Not a good way to start off any new public GPL projects, in my opinion. The current project I’m working on is an Android app that calculates star trails meant to help photographers get or avoid that in their night time photos. For now I’m going to skip some of the import process because I didn’t document it exactly. It’s been mostly trial and error as I poke around Android Studio post import. The Android Studio import process… Removing Admob Google Play code before the project would run at all. After removing dependencies, it kind of worked, but when running it in the emulator it shows a pop-up message saying that the app was developed for an old version of Android. Going through the process of updating code to match current best practices… I had the IDE convert the ...

Printing to file in Linux WINE

I noticed that this post has been sitting as a draft since 2011. At this point I have no idea if it’s useful or what I was even doing, but I might as well make it public in case someone can find it helpful! So I’ve been trying to get one of those PDF print drivers working in WINE without success. I then came upon a process that might work. When printing you need to select the checkbox “Print to file” that creates a .prn file. Just Linux things... I was using a program that only has printing facilities, but I want to export around 100 pages of text and images. Once you have the .prn (postscript) file, you can do any number of things to it. In my case I want the postscript file to be converted to HTML. I am also considering PDF format because that has more conversion options to eventually get me to HTML or plain text. sudo apt-get install cups-pdf Or it looks like that package might have changed to this… sudo apt-get install printer-driver-cups-pdf Where PDFs would be generated in /home/...