After a long week of rest, I went to the sea at HuaHin with my friends, then went back to see my mom.
All about rest and relax.
Now back to business, I have gotten my hand dirty again.
I have looked into the bloom post-processing sample from creator club. Not long that I decide to do some post-processing to enhance my scene. And that pretty result comes from the game I played along a week ago, it’s “Elder Scrolls IV: Oblivion”.
All the shinny thing and burn-out stuff just enough get me to this.
The sample from creator club just explain clearly enough for me to go on.
To do the bloom post-processing, you need to render the scene multiple times based on the step and passes of your algorithm too. For the sample case, it uses 4 passes, see the following
Pass 1: Render the scene with the lower-than bright threshold value excluded.
Pass 2-3: Render the scene from pass 1 but apply it with blur effect. No matter your blur algorithm is, you just need to blur it. (The sample use Gaussian Blur algorithm separately for vertical and horizontal, thus we can count for this 2 passes.)
Pass 4: Combine the original scene with the result from pass: 2-3.
Now you will have more shinny effect with the brighter area coming all around.
Let’s see the actual shader code implement for this. (Most of them come from the sample itself, but you know I dont want to just copy and paste it, I want to understand the concept and reason of usage behind, so you can adapt and do more with them ;)
Following is the effect that I have apply, pass by pass,
BloomExtract
float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0 { //look up the color from the texture float4 color = tex2D(sceneSampler, texCoord); //extract the bright pixel (only those that higher than BloomThreshold will still hold) return saturate( (color - BloomThreshold) / (1 - BloomThreshold) ); }
If we want to exclude out the “not enough bright pixel” then look at the line
saturate( (color - BloomThreshold) / (1 - BloomThreshold) )
This will cut out the below threshold value and recalculate from the whole less.
BloomBlur
//Width to sampling-pixels float blurMag; //Alpha value float alpha; const float2 offsets[12] = { -0.326212, -0.405805, -0.840144, -0.073580, -0.695914, 0.457137, -0.203345, 0.620716, 0.962340, -0.194983, 0.473434, -0.480026, 0.519456, 0.767022, 0.185461, -0.893124, 0.507431, 0.064425, 0.896420, 0.412458, -0.321940, -0.932615, -0.791559, -0.597705, }; float4 PixelShader(float2 texCoord : TEXCOORD0) : COLOR0 { //look up the color from the texture float4 sum = tex2D(sceneSampler, texCoord); //accumulate from the 12-kernel for(int i=0; i<12; i++) { sum += tex2D(sceneSampler, texCoord + blurMag * offsets[i]); } //average them sum /= 13; return sum; }
You see the offsets array with the 12 of 2 space coordinate inside it, it will be the displacement offseter for us. (Note that this blur I used is different from the one use in the creator sample).
What we gonna do here is, for each pixel we will calculate the 12-neighbor pixels’ color and sum it, then average, after that we get the color that should be represent for individual pixel we working on.
By offset the texture’s color sampling from the offsets array, multiply it with the default texture’s coordinate.
Note: I think the value inside the offsets array can be arbitrary set, but I have not test it yet. So you have the opportunity to test it.
By changing from 12-pixel to higher number, you will get more and more blur result and more accurate, but if the number of pixels is more than enough then you may get the same color representing that individual pixel, thus it means that by setting the number of pixel to 30 you may get the same result at 60 (this comes from my bit of theory ).
CombineFinal
This one will combine the result from the blur pass(already extracted), with the original scene.
The theory comes to play again, as you can see in the creator club sample, it said that human’s eye is sensitive to green light, more than blue light, thus we will lerp the color for this pass too.
// Helper for modifying the saturation of a color. float4 AdjustSaturation(float4 color, float saturation) { // The constants 0.3, 0.59, and 0.11 are chosen because the // human eye is more sensitive to green light, and less to blue. float grey = dot(color, float3(0.3, 0.59, 0.11)); return lerp(grey, color, saturation); } float4 PixelShader(float2 texCoord: TEXCOORD): COLOR0 { float4 scene = tex2D(sceneSampler, texCoord); float4 bloom = tex2D(postSampler, texCoord); //adjust color saturation and intensity scene = AdjustSaturation(scene, sceneSaturation) * sceneIntensity; bloom = AdjustSaturation(bloom, bloomSaturation) * bloomIntensity; //darken the scene image where there is a lot of bloom //this will avoid the too much of burn-out effect scene *= (1 - (saturate(bloom)-0.0001)); return scene + bloom; }
You also see the saturation value for both scene, and bloom part, and also the intensity for both of them.
By having these variables, we can change the effect-looking of the scene to various way => like burn-out, stable, or more and more shinny.
See the line,
scene *= (1 - (saturate(bloom)-0.0001));
it means that we will lower the brightness of color of the scene as always, this will let the bloom more takes into effect. If there is alot of bloom then the more we will lower the original scene. Also I subtract (bias) with the value of 0.0001, in the case where saturate(bloom) equals to 1, i dont want the original scen color to just disappear.
We are there anyway. Doing the post-processing is fun to play.
I have some images below to show to you too.
It looks similar to the one from Catalin Zima at Ziggyware, because I have used the same models and his deferred shading method.
And also the algorithm of shader is more the same as the one from creator but I recoded in my own way (just to learn), as you can see I have change the blur algorithm to another one that easier (adapt a little bit from Jamezila by cut out the bloom part but remain only the blur part.)
Okay, enough talk already, below is the images,
- Extract bright pixel
- Blur with 12-kernel
- Combine images
- Bloom 1
- Bloom 2
- Bloom 3
- Bloom 4
- Bloom 5
Until next time.
[Thanks for Catalin Zima for his great deferred shading, Jamezila for very simple but wonderful bloom/blur effect, Creator Club for great tutorial sample and models-usage.]










That is fantastic job!
I love it and I hope you will provide more technics on this site.
Thank you Haxpor..
[Mz] - ^^z