Generating animated clouds using Perlin noise.

by Pandafox | Posted in ActionScript 3, Tutorials

This tutorial is all about how you can easily generate neat bitmap data with perlin noise.
Perlin noise is a popular technique for making textures from pseudo-random numbers. This may (or may not) sound scary, but luckily for us, ActionScript 3 will handle most of the maths for us.

We do, of course, have a LIVE DEMO of the result for you to play with.

If you fill a bitmap with Perlin noise, chances are that you will end up with a texture that looks like a bunch of clouds (depending on your input parameters).

We’ll take a more in-depth look at perlin noise in another, non-ActionScript spesific, tutorial. But until we crank out that tutorial, you may want to take a look at this splendid page where Perlin noise is explained very well with lots of pseudo-code.

The code

Lets start by setting up a nice little stage with a listener that calls an update-function every frame:

package {
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;


	[SWF(width="320", height="240", frameRate="15", backgroundColor="0xffffff")]
	public class PerlinNoise extends Sprite
	{
		public function PerlinNoise()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
		
			stage.addEventListener(Event.ENTER_FRAME, update);
		}
		
		public function update(e:Event):void
		{
			
		}
	}
}

So, it’s nothing special yet. Now, what we have to do is to add a Bitmap-object and give it some BitmapData in which we can generate our marvelous fluffly clouds.

We add a BitmapData-variable along with two others to our class like so:

public class PerlinNoise extends Sprite
{
		private var _bitmapData:BitmapData;
		private var _seed:Number;
		private var _n:Number;

We will, of course, store our BitmapData-reference in _bitmapData, but what’s the deal with the two other variables?
Well, when we’re going to generate our noise, we will have to seed the random number generator so that our clouds won’t look the same every time we run our program. But we’ll get back to this later.

As for the _n-variable, this will be used to animate our clouds. We will increase this value a little bit every frame and use it when we are generating our bitmap data. Again, we will get back to this later.

Next up, setting up our bitmap and initializing our variables:

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;

	[SWF(width="320", height="240", frameRate="15", backgroundColor="0xffffff")]
	public class PerlinNoise extends Sprite
	{
		private var _bitmapData:BitmapData;
		private var _seed:Number;
		private var _n:Number;
		
		public function PerlinNoise()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
		
			_bitmapData = new BitmapData(320, 240);
			addChild(new Bitmap(_bitmapData));
			
			_n = 0;
			_seed = Math.floor(Math.random()*999);
			
			stage.addEventListener(Event.ENTER_FRAME, update);
		}
		
		public function update(e:Event):void
		{

		}
	}
}

We set up our BitmapData-instance with a size matching the stage size and a new Bitmap instance is created (with our BitmapData-instance as its bitmap data source) and added to the stage.

Our counter-thingy, _n, is set to 0 and _seed is set to a random number between 0 and 999. Great, believe me or not, we’re almost done!

The only thing we have to do now, is to add two things to our update-function:

public function update(e:Event):void
{
	_n += 2;
	_bitmapData.perlinNoise(
		296, // baseX:Number
		64, // baseY:Number
		6, // numOctaves:uint
		_seed, // randomSeed:int
		false, // stitch:Boolean
		true, // fractalNoise:Boolean
		BitmapDataChannel.ALPHA, // channelOptions:uint
		true, //  grayScale:Boolean
		[new Point(-_n, 0), new Point(-_n, 0)] // offsets:Array
	);
}

(Remember to import flash.geom.Point and flash.display.BitmapDataChannel)

Okay, this beast of a function probably requires some more description other than parameter names and datatypes, so here goes:

baseX:Number and baseY:Number

These parameters are the frequencies the noise-generators should use. They are basically used to set the size of the generated noise (if that makes any sense to you). We want our clouds to look stretched out, along the x-axis so we make sure baseX is a bigger than baseY. Try playing around with these parameter to get a sense of whats going on here.

numOctaves:uint

If you have done our homework on Perlin noise, you would know that the noise is generated by combining the output of multiple noise functions (or octaves as they are called) where the first octaves have low wavelength and high amplitude and the following octaves have decreasing wavelength and amplitude values. So the more octaves you add, the more detailed the noise gets.

randomSeed:int

This value is used by the noise functions/octaves to generate noise. If this values stays the same, you will end up with the same clouds every time you run the program.

stitch:Boolean

By setting stitch to true, the method will attempt to make the bitmap data tileable with seamless edges.

fractalNoise:Boolean

By setting this parameter to true, fractal noise will be used, rather than tubulence.
Long story short: Fractal noise = smoother.

channelOptions:uint

Here we specify which channels the noise should be generated in. We just want a bunch of white white/gray clouds with a transparent background, so we just use the alpha channel. If you want to use, for example, the red and the blue channel, you can combine them by using the OR-operator (“|”) like so:

BitmapDataChannel.RED | BitmapDataChannel.BLUE

grayScale:Boolean

Yep, you’re probably already guessed it. This if this parameter is set to true the final bitmap will be converted to a grayscale image, which is what we want in this case.

offsets:Array

This is an array of offsets which are applied to the noise functions.


By “animating” these offsets, it will look like our clouds are floating across the screen. In this case, we just add two offsets, which will be applied to the functions with the lowest frequency.

Finishing up

Alrighty, if you try running the code we’ve written so far, all you’ll see is some boring gray stuff floating across the screen. So, to make things a little more interesting, we’ll just finish up our code by embedding a photo underneath our clouds:

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Point;
	
	[SWF(width="320", height="240", frameRate="15", backgroundColor="0xffffff")]
	public class PerlinNoise extends Sprite
	{
		private var _bitmapData:BitmapData;
		private var _seed:Number;
		private var _n:Number;
		
		[Embed(source="picture.jpg")]
		private var Picture:Class;
		
		public function PerlinNoise()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			// Background Picture
			addChild(new Picture);
			
			// Clouds
			_bitmapData = new BitmapData(320, 240);
			addChild(new Bitmap(_bitmapData));
			
			_n = 0;
			_seed = Math.floor(Math.random()*999);
			
			stage.addEventListener(Event.ENTER_FRAME, update);
		}
		
		public function update(e:Event):void
		{
			_n += 2;
			_bitmapData.perlinNoise(
				296, // baseX:Number
				64, // baseY:Number
				6, // numOctaves:uint
				_seed, // randomSeed:int
				false, // stitch:Boolean
				true, // fractalNoise:Boolean
				BitmapDataChannel.ALPHA, // channelOptions:uint
				true, //  grayScale:Boolean
				[new Point(-_n, 0), new Point(-_n, 0)] // offsets:Array
			);
		}
	}
}

And that’s it!

Again, click HERE for a live demo.