To maximise the bloom effect in my code, yet attempt to minimize the processing requirements, it's performed on an 8x8 downsampled image. The net result is that the 9 samples used by the bloom filter can cover 8x as much texture real-estate. The downside is that it can look a bit shit [grin]
(Click To Enlarge)
In the above diagram you can see (as the main image to the bottom right) the final composite image - with the red face of the cube producing a somewhat OTT blur [smile]. You can also see straight off that it's showing some nasty point-filtered artifacts [headshake].
So, I sat down during eXistenZ last night to implement my own linear filter. I thought I'd share it with you lot here on the chance it might be useful to someone [wink]
For the downsampling stages in the above diagram, the kernel is computed by adding a set of offsets to the incoming texture coordinate and sampling the incoming texture multiple times (in the case of a 3x3 downsample, 9 texture reads). These offsets are the reciprocal of the texture dimensions. This way you can get the addresses for the neighbouring pixels.
My angle on the filtering took this approach, but instead of just averaging the sampled pixels, weighting them by the proximity to the desired sampling point. The idea being to, as the name suggests, provide a linear interpolation between the sampled pixels.
I adopted a 5-point sampling pattern:
The middle of that pattern is the pixel defined by the incoming texture coordinate to the pixel shader - so there's no magic in getting that coordinate. The other 4 are simply loaded in using +/- of the reciprocal height or width (as appropriate).
The trick comes in, having read the additional 2 pairs (horizontal and vertical) to weighting them.
For the incoming texture coordinate, considering that the texture being read will only be point filtered, we might end up with a situation like so:
(The whole box is a single pixel, the coordinate marked is w.r.t. the pixel not the whole texture!)
For the initial sample it doesn't matter what coordinate "inside" the pixel is receieved as it'll still map to the same element. However, if we can somehow compute this "inside" ratio we can weight the neighbouring pixels appropriately. From the above diagram we'd want to bias the ABOVE sample by 0.2 (1.0 - 0.8) and the BELOW sample by 0.8. The LEFT sample should be weighted by 0.2 and the RIGHT sample by 0.8 (1.0 - 0.8)...
So I cooked up this HLSL helper function:
float GetBloomWeight( float tc, float rcpDimension )
return ( tc - ( floor( tc / rcpDimension ) * rcpDimension ) ) / rcpDimension;
The final result isn't perfect by any means - but have a look at this:
(Click To Enlarge)
The blocky effect is still there, but it's not quite as pronounced as the original, trivial, implementation was [smile]
On the downside it has added an extra 30 instructions (4 texture 26 arithmetic) to my pixel shader [oh]