Last week we took a look at how to create metaballs in Flash, now it’s time for another all-time classic: the specular bloom effect.

(source link: http://bit.ly/pCMLmU)

In the real world, blooming occurs because even a perfect lens can’t focus perfectly, causing adjacent parts of an image to bleed together. At normal light levels, this effect is too subtle to be noticeable. However, if a light source in the image is much brighter than the rest, the bright parts will bleed across the source’s edges, causing the familiar glow that’s seen in so many games throughout the last decade.

A common way of doing bloom is as follows:
Take the input image and create a thresholded version of it, setting pixels below a specific threshold to black. Then blur the result and add it to the original image (using ADD or SCREEN as the blend mode).

 

Get Adobe Flash player

 

This approach is fast and works well for many applications, but it does have one drawback: There is a very noticeable cut off point, where a light source starts to glow.

Implementing specular bloom via thresholding results in a noticeable point at which objects begin to glow (image source: http://bit.ly/qD0qWS)

Ideally, what we want from a bloom filter is that darker parts of the source image don’t bleed at all, moderately bright parts bleed a little and very bright parts bleed a lot, with a nice nonlinear curve for these transitions. While adding the image to a blurred version of itself satisfies some of these desires (because adding a bright part to itself will have a more drastic result than adding a dark part to itself), it would be nice if we could get rid of the cut-off point.

The way I’d like to suggest to do this is a little more computationally expensive, but it provides nice results, if you have the CPU cycles to spare:

Start by creating a grayscale version of the input image (using a ColorMatrixFilter) and then multiply it with itself several times.

Think of the MULTIPLY blend mode as taking two images whose RGB channels store values between 0 (black) and 1 (full brightness) and multiplying these values together. If you’re multiplying an image with itself, you’re effectively squaring its brightness values, which means an¬†initial value of 0.1 becomes 0.1*0.1 = 0.01, an initial value of 0.5 becomes 0.25, and an initial value of 1 stays at 1. With several multiplications, the result becomes even more drastic, producing a steep and nonlinear brightness falloff.

After these operations, the resulting grayscale image is multiplied with the original image, and the result is blurred and then additively blended over the input.

Here’s a little swf showing the result of the multiplication step and the final result of the procedure:

 

Get Adobe Flash player

 

And some code:

// TODO: set these:
var IMAGE_WIDTH:int;
var IMAGE_HEIGHT:int;
var sourceImage:BitmapData;

var numPasses:int = 4;
var blurRadius:Number = 8;

var result:BitmapData = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0x000000);
// create a grayscale version of the source image:
var grayScale:BitmapData = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, false, 0x000000);
var matrix : Array = [
  0.3, 0.59, 0.11, 0, 0,
  0.3, 0.59, 0.11, 0, 0,
  0.3, 0.59, 0.11, 0, 0,
  0,    0,    0,    1, 0];
grayScale.applyFilter(sourceImage, new Rectangle(0,0,IMAGE_WIDTH, IMAGE_HEIGHT), new Point(0,0),
  new ColorMatrixFilter(matrix)
);

// multiply the grayscale image with itself:
result.draw(grayScale);
for (var i:int = 0; i<numPasses; i++)
{
  // note: for larger numbers of passes (but less fine-grained control), you can 
  // optimize this by multiplying result with itself instead of the original grayScale.
        // if you need more fine-tuned control, try changing the brightness/contrast after each step.
  result.draw(grayScale, null, null, BlendMode.MULTIPLY);
}
// multiply with the source image to add color back in:
result.draw(sourceImage, null, null, BlendMode.MULTIPLY);

// blur the multiplied image
result.applyFilter(result, new Rectangle(0,0,IMAGE_WIDTH, IMAGE_HEIGHT), new Point(0,0), 
  new BlurFilter(blurRadius, blurRadius, BitmapFilterQuality.MEDIUM));
// add the source back in
result.draw(sourceImage, null, null, BlendMode.ADD);
									

Since the multiply operation and the ColorMatrixFilter are quite expensive and you might want to apply the multiplication several times, a step you can take to optimize the procedure is to scale down the image to half (or more) before applying the gray scale filter. Then you perform all other steps up to the blurring, after which you scale the image back up. Since the image would be blurry anyway, the scaling won’t be as noticeable (if it is, try scaling the image back up before the blur), and all the filtering done before it will be a lot cheaper since there are less pixels to crunch.

 

One Response to Flash effects: Two ways of creating real-time bloom effects in AS3

  1. Bruce_Jawn says:

    Nice job! Added to my collection of post-processing effects: http://bruce-lab.blogspot.com/p/post-processing.html

Leave a Reply

Your email address will not be published. Required fields are marked *

*


5 + eight =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>