Petri's profileDark Codex StudiosPhotosBlogListsMore Tools Help
    October 04

    XNA Shader Programming - Tutorial 24, Bloom

    XNA Shader Programming
    Tutorial 24 - Bloom post process shader
    Welcome to the XNA Shader Programming Tutorial 24. Today, we are going to implement a bloom effect. The shader will be a post process shader, and will bloom any given texture. I learned blooming from http://xna.creators.com, so this implementation is based on this example.
     
    What is Blooming?

    Well, blooming occurs when colors flows over to surrounding pixels, brightening or overexposing them in the process. The dark pixels on the edges of bright pixels will get affected by the bright pixels, making the dark pixels lose some details. Fig 24.2 shows the difference between a scene with bloom (left) and one without bloom (right).

      

    Fig 24.2 - Difference between bloom an non-bloom scene

     

    We will need a few render targets to archive the bloom effect. First is the original scene, then we will one texture that will contain the blooming and two textures that will contain the blurred version of the bloom scene.

     

    The 1st blur texture will blur the bloom texture once, and the 2nd blur texture will blur the 1st blur texture, creating a better looking blur. Our blur shader is not the best way of blurring but its good enough. If you want to create a more advances blur effect, look up “Gaussian blur”.

     

    The first thing we will do is to render the normal scene to a texture. Next, we need to extract the bright areas in the original scene and store it in another texture named BloomTexture.

    We will use a shader to extract the bright colors. What this shader will do is to get a pixel from the original scene, and based on a threshold variable, calculate if this color is bright enough for the blooming effect.

    saturate((Color - Threshold) / (1 - Threshold));

     

    Threshold will contain a value between 0.0 and 1.0, and is used to remove colorinformation from Color that is below Threshold.


    Fig 24.3 – the content of bloom texture

     

    Fig 24.3 contains our bloom texture, based on the original scene with a Threshold of 0,3f. As you can see, the scene removes everything dark( based on Threshold ) and keeps the rest.

    Listing 10 – Bloom.fx
    // This shader gets the areas that are bright. This will later be blured making bright spots "glow"

    sampler TextureSampler : register(s0);

     

    // Get the threshold of what brightness level we want to glow

    float Threshold = 0.3;

     

     

    float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0

    {

        float4 Color = tex2D(TextureSampler, texCoord);

       

        // Get the bright areas that is brighter than Threshold and return it.

        return saturate((Color - Threshold) / (1 - Threshold));

    }

     

     

    technique Bloom

    {

        pass P0

        {

                // A post process shader only needs a pixel shader.

            PixelShader = compile ps_2_0 PixelShader();

        }

    }

     

     

    Next, we will need to blur this scene. We will use the same blur effect as we used in tutorial 23 (blur), but this time we will use it two times to blur the scene a bit better. Fig 24.4 shows the 2x blurred version of our blooming texture.



    Fig 24.4 – Blurred version of our bloom texture

     

    The last thing we will do is to combine the original scene with the blurred bloom texture. We could just have rendered the original scene, and blended the bloom texture over it to combine the original scene with the bloom texture, but this would not look that good and we would have non flexibility in the combination process. So, what does this mean? You guessed right, we will make a new shader that will combine the two textures!

     

    This shader will have four parameters that you can use to control the combination process. These are the intensity of our bloom texture and the original scene texture, and the saturation of the original scene texture and the bloom textures.
    // Controls the Intensity of the bloom texture

    float BloomIntensity = 1.3;

     

    // Controls the Intensity of the original scene texture

    float OriginalIntensity = 1.0;

     

    // Saturation amount on bloom

    float BloomSaturation = 1.0;

     

    // Saturation amount on original scene

    float OriginalSaturation = 1.0;

     

    We give them a default value, but you can control this from the application that will use this shader.

     

    Next, we create a function that will help us with color saturation (the difference of a color against gray, or its own brightness). This function will saturate a given input color, based on a saturation value and the gray color we used to make a grayscale scene earlier in this article.

    float4 AdjustSaturation(float4 color, float saturation)

    {

        // We define gray as the same color we used in the grayscale shader

        float grey = dot(color, float3(0.3, 0.59, 0.11));

       

        return lerp(grey, color, saturation);

    }

     

    Let’s move on, and start on the PixelShader function. We start by getting the color from our blurred bloom texture, and the original scene.
    // Get our bloom pixel from bloom texture

    float4 bloomColor = tex2D(BloomSampler, texCoord);

     

    // Get our original pixel from ColorMap

    float4 originalColor = tex2D(ColorMapSampler, texCoord);

     

    Next, we use the AdjustSaturation function to adjust the bloomColor and originalColor based on the parameters we added to the shader.

    bloomColor = AdjustSaturation(bloomColor, BloomSaturation) * BloomIntensity;

    originalColor = AdjustSaturation(originalColor, OriginalSaturation) * OriginalIntensity;

     

    Using these parameters, you can make the original scene very dark, and only bloomed areas bright and vice versa. You should play around with these values to see how this works!

     

    We continue the pixel shader by darkening the original scene where the bloomColor is bright, so we avoid making these areas burned-out.

    originalColor *= (1 - saturate(bloomColor));

     

    Now, the final step is to combine the textures.

    return originalColor + bloomColor;

     

    No magic behind this, just return the two colors added together!

     

    And boom you got bloom! Pretty simple, right? All we do is really just combining a few simple shaders to create an advances effect.

     

    Listing 11 – CombineBloom.fx
    // This combines the bloom texture with the original scene texture.

    // BloomIntensity, OriginalIntensity, BloomSaturation and OriginalSaturation is used

    // to control the blooming effect.

    // This shader is based on the example in creators.xna.com, where I learned this technique.

     

    // Our bloom texture

    sampler BloomSampler : register(s0);

     

    // Our original SceneTexture

    texture ColorMap;

     

    // Create a sampler for the ColorMap texture using lianear filtering and clamping

    sampler ColorMapSampler = sampler_state

    {

       Texture = <ColorMap>;

       MinFilter = Linear;

       MagFilter = Linear;

       MipFilter = Linear;  

       AddressU  = Clamp;

       AddressV  = Clamp;

    };

     

    // Controls the Intensity of the bloom texture

    float BloomIntensity = 1.3;

     

    // Controls the Intensity of the original scene texture

    float OriginalIntensity = 1.0;

     

    // Saturation amount on bloom

    float BloomSaturation = 1.0;

     

    // Saturation amount on original scene

    float OriginalSaturation = 1.0;

     

     

    float4 AdjustSaturation(float4 color, float saturation)

    {

        // We define gray as the same color we used in the grayscale shader

        float grey = dot(color, float3(0.3, 0.59, 0.11));

       

        return lerp(grey, color, saturation);

    }

     

     

    float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0

    {

          // Get our bloom pixel from bloom texture

          float4 bloomColor = tex2D(BloomSampler, texCoord);

     

          // Get our original pixel from ColorMap

          float4 originalColor = tex2D(ColorMapSampler, texCoord);

       

        // Adjust color saturation and intensity based on the input variables to the shader

          bloomColor = AdjustSaturation(bloomColor, BloomSaturation) * BloomIntensity;

          originalColor = AdjustSaturation(originalColor, OriginalSaturation) * OriginalIntensity;

       

        // make the originalColor darker in very bright areas, avoiding these areas look burned-out

        originalColor *= (1 - saturate(bloomColor));

       

        // Combine the two images.

        return originalColor + bloomColor;

    }

     

     

    technique BloomCombine

    {

        pass P0

        {

            // A post process shader only needs a pixel shader.

            PixelShader = compile ps_2_0 PixelShader();

        }

    }

     

     

    YouTube - XNA Shader programming, Tutorial 24 - Bloom post process
       

    Source and Executable: Tutorial 24 - Bloom

    Comments

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.

    To add a comment, sign in with your Windows Live ID (if you use Hotmail, Messenger, or Xbox LIVE, you have a Windows Live ID). Sign in


    Don't have a Windows Live ID? Sign up

    Trackbacks

    The trackback URL for this entry is:
    http://digierr.spaces.live.com/blog/cns!2B7007E9EC2AE37B!860.trak
    Weblogs that reference this entry
    • None