Effect and Code

In this tutorial we show how to use two important methods of the BitmapData class, perlinNoise and ColorMatrixFilter. By combining the two, we obtain a realistic dynamic cloud effect. The color schemes of the sky and the clouds can easily be changed. The ffect presenteted here is very CPU intensive. If you wish to create a large animation or a cloud effect for mobile devices chack out our tutorial: AS3 Flash: Low CPU Clouds Animation - Perfect for Mobile.

Click the screen shot below or this link to open the effect in a new window:

Download

Download the well-commented source files corresponding to the effect above:

Comments and Code

Here is the simple Timeline code behind the effect. We keep comments within the code for greater clarity.

/*
We are declaring variables that we need. 'display' is a Sprite container that will contain all elements of our animation which are:
 
perlinData - a BitmapData to which a Perlin noise will be applied as well as a ColorMatrixFilter, cmf;
perlinBitmap - a Bitmap corresponding to perlinData;
blueBackground - a Shape with sky-colored fill.
 
In addition, 'display' contains the text 'flash&math' which is stored in the Library as a MovieClip and linked to AS3 under the name of mcText.
*/

var display:Sprite;

var perlinData:BitmapData;

var perlinBitmap:Bitmap;

var cmf:ColorMatrixFilter;

var blueBackground:Shape;

var skyColor:uint;

/*
The dimensions of display and of other objects.
*/

var displayWidth:Number;

var displayHeight:Number;

/*
The remaining variables will serve as parameters of our Perlin noise. We explain their meaning later on this page.
*/

var periodX:Number;

var periodY:Number;

var seed:int;

var offsets:Array;

var numOctaves:int;

/*
We initialize all variables within the function 'init'.
*/

init();

 

function init():void {

var i:int;

display = new Sprite();

this.addChild(display);

display.x=25;

display.y=30;

displayWidth = 500;

displayHeight = 1500;

 

//periodX, periodY, numOctaves determine properties of our Perlin noise and

//the appearance of the clouds. Smaller values for periods will result in

//higher horizontal or vertical frequencies which will produce smaller,

//more frequent clouds. Smaller number of octaves, numOctaves, will give clouds

//not as good-looking but much more CPU friendly, especially for larger images.

//Perlin noise is very CPU intensive. For example, by setting periodX=150,

// periodY=60, numOctaves=3, you will get a nice looking sky

//that will run at much higher FPS.

 

periodX=150;

periodY=150;

numOctaves = 5;

 

//skyColor is the color of the blue background. You can easily change it

//on the next line to a different solid fill. A few lines below, you can also

//replace fill by a gradient fill and create a gradient sky.

 

skyColor = 0x2255AA;

 

//We are creating a BitmapData object that supports transparency of pixels

//(parameter 'true') and a Bitmap with that BitmapData. We will apply

//a grayscale Perlin noise to perlinData. To our grayscale Perlin noise,

//we will apply a ColorMatrixFilter, cmf. This filter makes darker pixels

//in perlinData more transparent so blue sky shows through.

//After applying cmf, some pixels are more transparent than others but they

//are all turned to white. (See explanations later on the page.)

 

perlinData = new BitmapData(displayWidth,displayHeight,true);

perlinBitmap = new Bitmap(perlinData);

cmf = new ColorMatrixFilter([0,0,0,0,255, 0,0,0,0,255, 0,0,0,0,255, 1,0,0,0,0]);

blueBackground = new Shape();

blueBackground.graphics.beginFill(skyColor);

blueBackground.graphics.drawRect(0,0,displayWidth,displayHeight);

blueBackground.graphics.endFill();

display.addChild(blueBackground);

display.addChild(perlinBitmap);

var mc:McText = new McText();

display.addChild(mc);

mc.x = 0.5*(displayWidth);

mc.y = 0.5*(displayHeight);

var outline:GlowFilter=new GlowFilter(0xA8C2EE,1.0,1.25,1.25,10);

outline.quality=BitmapFilterQuality.MEDIUM;

mc.filters=[outline];

mc.alpha = 0.8;

//We choose randomly a 'seed' used in Perlin noise and create the offsets array.

seed = int(Math.random()*10000);

offsets = new Array();

for (i = 0; i<=numOctaves-1; i++) {

offsets.push(new Point());

}

this.addEventListener(Event.ENTER_FRAME, onEnter);

}

 

function onEnter(evt:Event):void {

var i:int;

//Update offsets of the Perlin noise which moves the clouds.

for (i = 0; i<=numOctaves-1; i++) {

offsets[i].x += 1;

offsets[i].y += 0.2;

}

//We create a grayscale Perlin noise in perlinData and apply

//the ColorMatrixFilter, cmf, to it.

perlinData.perlinNoise(periodX,periodY,numOctaves,seed,false,true,1,true,offsets);

perlinData.applyFilter(perlinData, perlinData.rect, new Point(), cmf);

}

AS3 BitmapData.perlinNoise Method

Perlin noise is based on a complicated algorithm that creates random-looking textures. The algorithm is purely deterministic; that is, the same parameters will produce identical results. Yet, because of its clever construction, the results will appear random. If you'd like to hear about the algorithm and its many uses from the author himself, you can find here Professor Ken Perlin's lecture.

In AS3, you can create BitampData objects in which color channel values are governed by a Perlin noise with parameters of your choice. Such a BitmapData 'primitive' filled with Perlin noise can then be used to create a large variety of image effects. Perlin noise 'primitive' is rarely used on its own. Most of the time it is combined with an instance of the ColorMatrixFilter, as in our example, or an instance of the DisplacementMapFilter. (A tutorial on Perlin noise and the DisplacementMapFilter is coming soon to Flash and Math.)

The method takes the following parameters:

perlinNoise(baseX:Number,baseY:Number,numOctaves:uint,randomSeed:int,
          stitch:Boolean,fractalNoise:Boolean,channelOptions:uint=7,
          grayScale:Boolean=false,offests:Array=null):void

  • baseX is reponsible for horizontal 'frequency' of the noise. The lower the value of baseX, the higher horizontal frequency. With low baseX, you will see plenty of small clouds spread horizontally.
  • baseY is reponsible for vertical 'frequency' of the noise. The lower the value of baseY, the higher vertical frequency.
  • numOctaves is crucial and it is most responsible (together with the size of the sky) for the CPU usage during your animation. Each next 'octave' is a separate layer of the noise. It has twice the frequecy of the prevoius octave and adds detail to the effect. We use numOctaves=5. With numOctaves=2 the clouds will look too smooth and unrealistic. With numOctave=3, they are acceptable but not great. You should choose settings appropriate to the size of your image. Each octave significantly adds to the CPU usage and slows down animation.
  • randomSeed the starting point for the algorithm. We choose it randomly but only once and keep constant on ENTER_FRAME. The motion is created by changing the offsets parameter.
  • stitch smoothes the edges of the image to make tiling of Perlin noise easier.
  • fractalNoise setting it to true or false dramatically alters the nature of the noise. Try experimenting by changing the code in clouds.fla.
  • channelOptions specifies to which color channels the noise is applied. It may be passed as integer: 1 for red channnel only, 3 for red and green, 7 for red, green and blue, 15 red, gree, blue and alpha. It is much easier to specify the channelOptions parameter using bitwise OR. For example, pass as channelOptions
    BitmapDataChannel.RED
    for red,
    BitmapDataChannel.RED | BitmapDataChannel.GREEN | BitmapDataChannel.BLUE
    for red, green and blue etc. In our case, channels do not matter much as we use grayscale noise.
  • grayScale set to true produces a grayscale noise, false gives multicolored noise.
  • offsets is an Array of Points. Each element of the Array corresponds to one octave and gives horizontal and vertical offset for that octave. This is the parameter that we change on ENTER_FRAME to create motion of our clouds.

To really learn the meaning of all the parameters, you have to experiment with them. In clouds.fla (included in the zip package) comment out the line within the 'onEnter' function that says

perlinData.applyFilter(perlinData, perlinData.rect, new Point(), cmf);

Test the movie. You see the grayscale Perlin noise with no fliters applied to it. Go back to the fla file and experiment with the parameters in

perlinData.perlinNoise(periodX,periodY,numOctaves,seed,false,true,1,true,offsets);

The ColorMatrixFilter

ColorMatrixFilter is an 'expanded' version of ColorTransform and gives you greater control over transforming color channels of pixels in a BitmapData object. In ColorTransform you have control only over each color channel separately by adjusting its multiplier and offset. ColorMatrixFilter makes it possible to alter one color channel using values of other color channels.

An instance of the ColorMatrixFilter is an array of 20 elements, say a[0],a[1],....a[19]. For example, our instance, cmf, is:

cmf = new ColorMatrixFilter([0,0,0,0,255, 0,0,0,0,255, 0,0,0,0,255, 1,0,0,0,0]);

When a ColorMatrixFilter is applied to a BitmapData object as in:

perlinData.applyFilter(perlinData, perlinData.rect, new Point(), cmf);

the color channels of each pixel in the source BitmapData are altered according to the following formula:

where srcR, srcG, etc. are color values of a source pixel, red, green, and so on.

If you look at our instance, cmf, carefully you will notice that the cmf uses the value of the red channel of the source pixel to assign alpha of the resulting pixel and turns all pixels to white. If you want to change the tint of your clouds, change the values 255, 255, 255 in cmf to represent a color you wish your clouds to be.

If you are mathematically inclined you may notice that ColorMatrixFilter performs matrix multiplication:

Back to Intermediate Tutorials              Back to Flash and Math Home

We welcome your comments, suggestions, and contributions. Click the Contact Us link below and email one of us.

Adobe®, Flash®, ActionScript®, Flex® are registered trademarks of Adobe Systems Incorporated.