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.
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 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();