Quantcast
Channel: Community | MonoGame - Latest topics
Viewing all articles
Browse latest Browse all 6821

Texture reduces when you apply the blur effect

$
0
0

@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

Read full topic


Viewing all articles
Browse latest Browse all 6821

Trending Articles