ActionScript 3

The Burning Man – bitmapData fire effect

Yesterday night it was too warm to sleep and I have no air conditioning at home, so I thought it was a good opportunity to make some experiments related to the high temperature… “the burning man” 😀

Here’s the algorithm in human language:
– pick up the image from the webcam
– perform a threshold on it to exclude (alpha 0) every pixel below the average skin amount of red (between 44 and 90 depending on the light)
– perform a second threshold to exclude (alpha 0) every pixel above the average skin amount of green and blue (between 3344 and 6677 depending on the light)
– draw the result onto a new bitmapdata with the ADD blendmode
– apply blur to the bitmapdata
– generate a perlin noise of one channel (i chose red)
– displace the bitmapdata towards -y
– apply again a displacement map filter to displace horizontally (i used a cos function to make the x displacement a little likely)
– apply a displacement map towards +y with just a bit less strenght than the previous “-y” one (this is to make the fire propagation much more likely)
– then merge the original webcam output with the displaced one…and you’ve the burning man! 🙂

here the actionscript implementation

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.BlendMode;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.filters.DisplacementMapFilter;
	import flash.filters.DisplacementMapFilterMode;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;

	[SWF(width="320",height="240",frameRate="31",backgroundColor="0x000000")]
	public class BurningMan extends Sprite
	{

		private var _cam:Camera;
		private var _vid:Video;

		private var _fireThreshold:BitmapData;
		private var _firePerlin:BitmapData;
		private var _fireOffset:Point;
		private var _fireSpeed:int = 20;
		private var _fireDisplaceUP:DisplacementMapFilter;
		private var _fireDisplaceDWN:DisplacementMapFilter;
		private var _fireDisplaceSrc:BitmapData;
		private var _fireBlur:BlurFilter;
		private var _fireColorTransform:ColorTransform;

		private var _fireMerge:BitmapData;

		private var _camBd:BitmapData;
		private var _b:Bitmap;

		private static const WIDTH:Number = 320;
		private static const HEIGHT:Number = 240;
		private static const ORIGIN:Point = new Point();
		private static const REFLECT:Matrix = new Matrix(-1,0,0,1,WIDTH,0);
		private static const RESIZE_2X:Matrix = new Matrix(2,0,0,2);

		private var _rect:Rectangle;

		public function BurningMan()
		{
			super();
			init();
		}

		private function init():void
		{
			_cam = Camera.getCamera();
			_cam.setMode(WIDTH,HEIGHT,31);
			_vid = new Video(WIDTH,HEIGHT);
			_vid.attachCamera(_cam);

			_fireOffset = new Point();
			_fireThreshold = new BitmapData(WIDTH,HEIGHT,true,0x00000000);
			_fireMerge = new BitmapData(WIDTH,HEIGHT,true,0x00000000);

			_firePerlin = new BitmapData(WIDTH*.5,HEIGHT*.5,false,0x000000);
			_fireDisplaceSrc = new BitmapData(WIDTH,HEIGHT,false,0x000000);
			_fireDisplaceUP = new DisplacementMapFilter(_fireDisplaceSrc,ORIGIN,BitmapDataChannel.RED,BitmapDataChannel.RED,0,-15,DisplacementMapFilterMode.CLAMP);
			_fireDisplaceDWN = new DisplacementMapFilter(_fireDisplaceSrc,ORIGIN,BitmapDataChannel.RED,BitmapDataChannel.RED,0,9,DisplacementMapFilterMode.CLAMP);
			_fireBlur = new BlurFilter(2,2,4);
			_fireColorTransform = new ColorTransform(1.3,1.1,1,.7);


			_camBd = new BitmapData(WIDTH,HEIGHT,true,0x00000000);
			_rect = _camBd.rect;
			_b = new Bitmap(_camBd);
			_firePerlin.lock();
			_fireMerge.lock();
			addChild(_b);


			addEventListener(Event.ENTER_FRAME,update);
		}

		private function update(e:Event=null):void
		{
			//locks
			_camBd.lock();

			_fireOffset.y+=_fireSpeed;
			_firePerlin.perlinNoise(_firePerlin.width*.125,_firePerlin.height*.125,1,0,true,false,BitmapDataChannel.RED,false,[_fireOffset])
			_fireDisplaceSrc.draw(_firePerlin,RESIZE_2X);

			_camBd.draw(_vid,REFLECT);
			_fireThreshold.setVector(_rect,_camBd.getVector(_rect));
			_fireThreshold.threshold(_fireThreshold,_rect,ORIGIN,"",0xFF003344,0x00000000,0x00FFFF,true);
			_fireMerge.draw(_fireThreshold,null,_fireColorTransform,BlendMode.ADD,_rect);

			_fireDisplaceUP.scaleX = Math.cos(((_fireOffset.y/(2<<3))%360)*180/Math.PI)*2;
			_fireDisplaceDWN.scaleX = -_fireDisplaceUP.scaleX;
			_fireMerge.applyFilter(_fireMerge,_fireMerge.rect,ORIGIN,_fireBlur);
			_fireMerge.applyFilter(_fireMerge,_rect,ORIGIN,_fireDisplaceUP);
			_fireMerge.applyFilter(_fireMerge,_rect,ORIGIN,_fireDisplaceDWN);
			_camBd.draw(_fireMerge,null,_fireColorTransform,BlendMode.HARDLIGHT,_rect);

			//unlocks
			_camBd.unlock();
		}
	}
}

here’s a preview of myself empowered by the force of the fire!:D
burning man
click the photo to see the demo in action

Advertisements

2 thoughts on “The Burning Man – bitmapData fire effect

  1. Hi, it looks nice, but it would like liner if I could view it, but I cant. I dont have any web camera. It wasn’t necessary to had one. Could you send me instructions how to change the code a little to launch it without camera?

  2. it’s quite simple,
    just remove all occurrencies of video and camera and put a reference to the image you want to apply the effect to within the update method (_camBd.draw(yourImage))

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s