@MarlonSecundo wrote:
After
Before
Class Game1
public class Game1 : Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; private const int BLUR_RADIUS = 7; private const float BLUR_AMOUNT = 2.0f; private SpriteFont spriteFont; private Texture2D texture; private RenderTarget2D renderTarget1; private RenderTarget2D renderTarget2; private Vector2 fontPos; private KeyboardState currentKeyboardState; private KeyboardState prevKeyboardState; private GaussianBlur gaussianBlur; private int windowWidth; private int windowHeight; private int renderTargetWidth; private int renderTargetHeight; private int frames; private int framesPerSecond; private TimeSpan elapsedTime = TimeSpan.Zero; private bool displayHelp; private bool enableGaussianBlur; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; Window.Title = "XNA 4.0 Gaussian Blur"; IsMouseVisible = true; IsFixedTimeStep = false; } protected override void Initialize() { // Setup the window to be a quarter the size of the desktop. windowWidth = GraphicsDevice.DisplayMode.Width / 2; windowHeight = GraphicsDevice.DisplayMode.Height / 2; // Setup frame buffer. graphics.SynchronizeWithVerticalRetrace = false; graphics.PreferredBackBufferWidth = windowWidth; graphics.PreferredBackBufferHeight = windowHeight; graphics.ApplyChanges(); // Position the text. fontPos = new Vector2(1.0f, 1.0f); // Setup the initial input states. currentKeyboardState = Keyboard.GetState(); // Create the Gaussian blur filter kernel. gaussianBlur = new GaussianBlur(this); gaussianBlur.ComputeKernel(BLUR_RADIUS, BLUR_AMOUNT); base.Initialize(); } private void InitRenderTargets() { // Since we're performing a Gaussian blur on a texture image the // render targets are half the size of the source texture image. // This will help improve the blurring effect. renderTargetWidth = texture.Width / 2; renderTargetHeight = texture.Height / 2; renderTarget1 = new RenderTarget2D(GraphicsDevice, renderTargetWidth, renderTargetHeight, false, GraphicsDevice.PresentationParameters.BackBufferFormat, DepthFormat.None); renderTarget2 = new RenderTarget2D(GraphicsDevice, renderTargetWidth, renderTargetHeight, false, GraphicsDevice.PresentationParameters.BackBufferFormat, DepthFormat.None); // The texture offsets used by the Gaussian blur shader depends // on the dimensions of the render targets. The offsets need to be // recalculated whenever the render targets are recreated. gaussianBlur.ComputeOffsets(renderTargetWidth, renderTargetHeight); } private bool KeyJustPressed(Keys key) { return currentKeyboardState.IsKeyDown(key) && prevKeyboardState.IsKeyUp(key); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); spriteFont = Content.Load<SpriteFont>(@"Fonts\DemoFont"); texture = Content.Load<Texture2D>(@"Textures\lena"); InitRenderTargets(); } private void ProcessKeyboard() { prevKeyboardState = currentKeyboardState; currentKeyboardState = Keyboard.GetState(); if (KeyJustPressed(Keys.Escape)) this.Exit(); if (KeyJustPressed(Keys.Space)) enableGaussianBlur = !enableGaussianBlur; if (KeyJustPressed(Keys.H)) displayHelp = !displayHelp; if (currentKeyboardState.IsKeyDown(Keys.LeftAlt) || currentKeyboardState.IsKeyDown(Keys.RightAlt)) { if (KeyJustPressed(Keys.Enter)) ToggleFullScreen(); } } private void ToggleFullScreen() { int newWidth = 0; int newHeight = 0; graphics.IsFullScreen = !graphics.IsFullScreen; if (graphics.IsFullScreen) { newWidth = GraphicsDevice.DisplayMode.Width; newHeight = GraphicsDevice.DisplayMode.Height; } else { newWidth = windowWidth; newHeight = windowHeight; } graphics.PreferredBackBufferWidth = newWidth; graphics.PreferredBackBufferHeight = newHeight; graphics.ApplyChanges(); } protected override void UnloadContent() { renderTarget1.Dispose(); renderTarget1 = null; renderTarget2.Dispose(); renderTarget2 = null; } protected override void Update(GameTime gameTime) { if (!IsActive) return; ProcessKeyboard(); UpdateFrameRate(gameTime); base.Update(gameTime); } private void UpdateFrameRate(GameTime gameTime) { elapsedTime += gameTime.ElapsedGameTime; if (elapsedTime > TimeSpan.FromSeconds(1)) { elapsedTime -= TimeSpan.FromSeconds(1); framesPerSecond = frames; frames = 0; } } private void IncrementFrameCounter() { ++frames; } private void DrawText() { StringBuilder buffer = new StringBuilder(); if (displayHelp) { buffer.AppendLine("Press SPACE to enable/disable Gaussian blur"); buffer.AppendLine("Press ALT and ENTER to toggle full screen"); buffer.AppendLine("Press ESCAPE to exit"); buffer.AppendLine(); buffer.AppendLine("Press H to hide help"); } else { buffer.AppendFormat("FPS: {0}\n", framesPerSecond); buffer.AppendLine(); buffer.AppendFormat("Radius: {0}\n", gaussianBlur.Radius); buffer.AppendFormat("Sigma: {0}\n", gaussianBlur.Sigma.ToString("f2")); buffer.AppendLine(); buffer.AppendLine("Press H to display help"); } spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend); spriteBatch.DrawString(spriteFont, buffer.ToString(), fontPos, Color.Yellow); spriteBatch.End(); } protected override void Draw(GameTime gameTime) { if (!IsActive) return; if (enableGaussianBlur) { Texture2D result = gaussianBlur.PerformGaussianBlur(texture, renderTarget1, renderTarget2, spriteBatch); Rectangle rectangle = new Rectangle(0, 0, texture.Width, texture.Height); GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); spriteBatch.Draw(result, rectangle, Color.White); spriteBatch.End(); } else { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); spriteBatch.Draw(texture, new Rectangle(0, 0, texture.Width, texture.Height), Color.White); spriteBatch.End(); } DrawText(); base.Draw(gameTime); IncrementFrameCounter(); }
Class GaussianBlur
public class GaussianBlur { private Game game; private Effect effect; private int radius; private float amount; private float sigma; private float[] kernel; private Vector2[] offsetsHoriz; private Vector2[] offsetsVert; /// <summary> /// Returns the radius of the Gaussian blur filter kernel in pixels. /// </summary> public int Radius { get { return radius; } } /// <summary> /// Returns the blur amount. This value is used to calculate the /// Gaussian blur filter kernel's sigma value. Good values for this /// property are 2 and 3. 2 will give a more blurred result whilst 3 /// will give a less blurred result with sharper details. /// </summary> public float Amount { get { return amount; } } /// <summary> /// Returns the Gaussian blur filter's standard deviation. /// </summary> public float Sigma { get { return sigma; } } /// <summary> /// Returns the Gaussian blur filter kernel matrix. Note that the /// kernel returned is for a 1D Gaussian blur filter kernel matrix /// intended to be used in a two pass Gaussian blur operation. /// </summary> public float[] Kernel { get { return kernel; } } /// <summary> /// Returns the texture offsets used for the horizontal Gaussian blur /// pass. /// </summary> public Vector2[] TextureOffsetsX { get { return offsetsHoriz; } } /// <summary> /// Returns the texture offsets used for the vertical Gaussian blur /// pass. /// </summary> public Vector2[] TextureOffsetsY { get { return offsetsVert; } } /// <summary> /// Default constructor for the GaussianBlur class. This constructor /// should be called if you don't want the GaussianBlur class to use /// its GaussianBlur.fx effect file to perform the two pass Gaussian /// blur operation. /// </summary> public GaussianBlur() { } /// <summary> /// This overloaded constructor instructs the GaussianBlur class to /// load and use its GaussianBlur.fx effect file that implements the /// two pass Gaussian blur operation on the GPU. The effect file must /// be already bound to the asset name: 'Effects\GaussianBlur' or /// 'GaussianBlur'. /// </summary> public GaussianBlur(Game game) { this.game = game; try { effect = game.Content.Load<Effect>(@"Effects\GaussianBlur"); } catch (ContentLoadException) { effect = game.Content.Load<Effect>("GaussianBlur"); } } /// <summary> /// Calculates the Gaussian blur filter kernel. This implementation is /// ported from the original Java code appearing in chapter 16 of /// "Filthy Rich Clients: Developing Animated and Graphical Effects for /// Desktop Java". /// </summary> /// <param name="blurRadius">The blur radius in pixels.</param> /// <param name="blurAmount">Used to calculate sigma.</param> public void ComputeKernel(int blurRadius, float blurAmount) { radius = blurRadius; amount = blurAmount; kernel = null; kernel = new float[radius * 2 + 1]; sigma = radius / amount; float twoSigmaSquare = 2.0f * sigma * sigma; float sigmaRoot = (float)Math.Sqrt(twoSigmaSquare * Math.PI); float total = 0.0f; float distance = 0.0f; int index = 0; for (int i = -radius; i <= radius; ++i) { distance = i * i; index = i + radius; kernel[index] = (float)Math.Exp(-distance / twoSigmaSquare) / sigmaRoot; total += kernel[index]; } for (int i = 0; i < kernel.Length; ++i) kernel[i] /= total; } /// <summary> /// Calculates the texture coordinate offsets corresponding to the /// calculated Gaussian blur filter kernel. Each of these offset values /// are added to the current pixel's texture coordinates in order to /// obtain the neighboring texture coordinates that are affected by the /// Gaussian blur filter kernel. This implementation has been adapted /// from chapter 17 of "Filthy Rich Clients: Developing Animated and /// Graphical Effects for Desktop Java". /// </summary> /// <param name="textureWidth">The texture width in pixels.</param> /// <param name="textureHeight">The texture height in pixels.</param> public void ComputeOffsets(float textureWidth, float textureHeight) { offsetsHoriz = null; offsetsHoriz = new Vector2[radius * 2 + 1]; offsetsVert = null; offsetsVert = new Vector2[radius * 2 + 1]; int index = 0; float xOffset = 1.0f / textureWidth; float yOffset = 1.0f / textureHeight; for (int i = -radius; i <= radius; ++i) { index = i + radius; offsetsHoriz[index] = new Vector2(i * xOffset, 0.0f); offsetsVert[index] = new Vector2(0.0f, i * yOffset); } } /// <summary> /// Performs the Gaussian blur operation on the source texture image. /// The Gaussian blur is performed in two passes: a horizontal blur /// pass followed by a vertical blur pass. The output from the first /// pass is rendered to renderTarget1. The output from the second pass /// is rendered to renderTarget2. The dimensions of the blurred texture /// is therefore equal to the dimensions of renderTarget2. /// </summary> /// <param name="srcTexture">The source image to blur.</param> /// <param name="renderTarget1">Stores the output from the horizontal blur pass.</param> /// <param name="renderTarget2">Stores the output from the vertical blur pass.</param> /// <param name="spriteBatch">Used to draw quads for the blur passes.</param> /// <returns>The resulting Gaussian blurred image.</returns> public Texture2D PerformGaussianBlur(Texture2D srcTexture, RenderTarget2D renderTarget1, RenderTarget2D renderTarget2, SpriteBatch spriteBatch) { if (effect == null) throw new InvalidOperationException("GaussianBlur.fx effect not loaded."); Texture2D outputTexture = null; Rectangle srcRect = new Rectangle(0, 0, srcTexture.Width, srcTexture.Height); Rectangle destRect1 = new Rectangle(0, 0, renderTarget1.Width, renderTarget1.Height); Rectangle destRect2 = new Rectangle(0, 0, renderTarget2.Width, renderTarget2.Height); // Perform horizontal Gaussian blur. game.GraphicsDevice.SetRenderTarget(renderTarget1); effect.CurrentTechnique = effect.Techniques["GaussianBlur"]; effect.Parameters["weights"].SetValue(kernel); effect.Parameters["colorMapTexture"].SetValue(srcTexture); effect.Parameters["offsets"].SetValue(offsetsHoriz); spriteBatch.Begin(0, BlendState.Opaque, null, null, null, effect); spriteBatch.Draw(srcTexture, destRect1, Color.White); spriteBatch.End(); // Perform vertical Gaussian blur. game.GraphicsDevice.SetRenderTarget(renderTarget2); outputTexture = (Texture2D)renderTarget1; effect.Parameters["colorMapTexture"].SetValue(outputTexture); effect.Parameters["offsets"].SetValue(offsetsVert); spriteBatch.Begin(0, BlendState.Opaque, null, null, null, effect); spriteBatch.Draw(outputTexture, destRect2, Color.White); spriteBatch.End(); // Return the Gaussian blurred texture. game.GraphicsDevice.SetRenderTarget(null); outputTexture = (Texture2D)renderTarget2; return outputTexture; }
}
File GaussianBlur.fx
#define RADIUS 7 #define KERNEL_SIZE (RADIUS * 2 + 1) //----------------------------------------------------------------------------- // Globals. //----------------------------------------------------------------------------- float weights[KERNEL_SIZE]; float2 offsets[KERNEL_SIZE]; //----------------------------------------------------------------------------- // Textures. //----------------------------------------------------------------------------- texture colorMapTexture; sampler2D colorMap = sampler_state { Texture = <colorMapTexture>; MipFilter = Linear; MinFilter = Linear; MagFilter = Linear; }; //----------------------------------------------------------------------------- // Pixel Shaders. //----------------------------------------------------------------------------- float4 PS_GaussianBlur(float2 texCoord : TEXCOORD) : COLOR0 { float4 color = float4(0.0f, 0.0f, 0.0f, 0.0f); for (int i = 0; i < KERNEL_SIZE; ++i) color += tex2D(colorMap, texCoord + offsets[i]) * weights[i]; return color; } //----------------------------------------------------------------------------- // Techniques. //----------------------------------------------------------------------------- technique GaussianBlur { pass { PixelShader = compile ps_4_0_level_9_1 PS_GaussianBlur(); } }
Posts: 3
Participants: 2