Animeret Perlin noise


Lær hvordan du hurtigt og nemt kan lave et animeret Perlin noise map.

Beklager, du har ikke den krævede version af Flash Player. Du kan hente den fra http://www.adobe.com/go/getflash/

Intro

Denne guide er beregnet som en introduktion til og nem opsætning af et animeret Perlin noise. Den er baseret på arbejdet af Seb Lee-Delisle(http://www.sebleedelisle.com/2007/11/3d-perlin-noise-in-flash/), Ron Valstar(http://www.sjeiti.com/?p=305) og selvfølgelig Ken Perlin(http://mrl.nyu.edu/~perlin/doc/oscar.html).

Perlin noise er et specialiseret slags noise som har en smoothing/gradient:

Denne type noise kan bruges til mange ting, både direkte visuelt, men også til at styre visuelle elementer eller pathing, flocking og animation af objekter. Da Perlin noise består af gradienter egner det sig godt til at generere organiske og flydende animationer.

Perlin noise som 3D terræn:
http://larry.onlinekarma.net/tutorials/Sandy3/Terrain/bin/WaterTerrain.s...

Perlin noise til at lave flocking:
http://www.kelvinluck.com/2007/11/flash-on-the-beach-and-some-perlin-noi...

Princippet

Her i guiden vil jeg fokusere på at lave et animeret Perlin noise ligesom det Seb har lavet(http://www.sebleedelisle.com/2007/11/3d-perlin-noise-in-flash/).

Princippet bag et animeret Perlin noise er at generere et 3D Perlin noise og så animere ved at tage "slices" på z-aksen, dette giver en flydende animation, hvorimod hvis man bare gentegnede et 2D Perlin noise med et nyt seed(tilfældighed) ville det give et meget hakkende resultat.

Flash understøtter som standard kun 2D Perlin noise med BitmapData.perlinNoise(), men heldigvis har Ron Valstar konverteret Ken Perlin's originale 3D Perlin Noise til AS3 hvorefter Mario Klingemann har lavet nogle optimeringer af klassen. Den kan downloades her: http://www.quasimondo.com/archives/000672.php

Her er et eksempel lavet af Ron Valstar hvor klassen er brugt, den viser også de forskellige parametre man kan stille på:

Beklager, du har ikke den krævede version af Flash Player. Du kan hente den fra http://www.adobe.com/go/getflash/

Inden jeg viser hele koden der skal til for at få et animeret Perlin Noise til at køre så lad os lige kigge på et par kodestumper.

Perlin settings

Først de settings vi laver på OptimizedPerlin klassen(den vi downloadede), det er de samme parametre som vi havde i ovenstående eksempel.

// settings for the perlin noise renderer
// size of areas, integers, higher = smaller areas = more detailed
OptimizedPerlin.octaves = 3;
// smoothing, values: 0-1, lower = more smooth
OptimizedPerlin.falloff = 0.5;
// generate perlin seed(randomness)
OptimizedPerlin.seed = Math.round(Math.random() * 123);

Octaves er mængden/størrelsen af "områder" og dermed også detaljegraden, højere betyder flere "områder" mere detaljeret. Falloff er mængden af smoothing(gradient graden), lavere betyder mere smoothing. Seed er hvilken "opsætning" man får, så man sætter den til et random tal hver gang til at få et forskelligt resultet.

// create bitmapdata that we will draw perlin map onto, keep size small for fast render
_perlinData = new BitmapData(50,25,false,0x000000);
 
// create bitmap for perlin bitmapdata, smoothing is important cause it will be scaled
var perlinBitmap:Bitmap = new Bitmap(_perlinData,"auto",true);

Det bitmapData vi genererer vores Perlin noise på skal være meget lille(her 50x25px) for at vi kan generere vores noise hurtigt nok til at animere det, men heldigvis kan vi bare skalere vores bitmap op med smoothing på og stadig få et godt resultet.

Draw Perlin

Her er metoden som genererer og tegner vores Perlin noise:

// DRAW PERLIN
private function drawPerlin():void
{
	// lock the bitmapdata (block updates)
	_perlinData.lock();
 
	// run through each pixel in the bitmapdata
	for (var i:Number = 0; i < _perlinData.width; ++i) 
	{
		for (var j:Number = 0; j < _perlinData.height; ++j) 
		{
			// get value from perlin noise (0-1)
			var noiseValue:Number = OptimizedPerlin.noise( i/_perlinData.width, j/_perlinData.height, _zValue );
			// convert value to color amount (0-255)
			var noiseColorValue:int = int( (.5+1.*(noiseValue-.5))*255 );
			// create blue color between full blue and full white
			var color:uint = noiseColorValue << 16 | noiseColorValue << 8 | 255;
			// set color on bitmapdata pixel
			_perlinData.setPixel(i, j, color);
		}
	}
 
	//unlock the bitmapdata
	_perlinData.unlock();
}

Vi starter og slutter med at låse vores bitmapdata så der ikke bliver sendt updates til vores bitmap for hver pixel vi løber igennem. Efter den er låst så løber vi igennem hver pixel vi har i vores bitmapdata og får Perlin noise værdien med OptimizedPerlin.noise(), som tager imod x, y og z parametre som alle skal være meget lave tal. Herefter konverterer vi værdien fra 0-1 til 0-255 så vi kan bruge det til at lave en farve, så laver vi en blå farve mellem helt blå og helt hvid, og til sidst tegner vi farven på den pixel i vores bitmapdata som vi er kommet til.

Enter Frame

Til sidst har vi den funktion der sørger for at animere vores Perlin noise, den bliver kaldt hver frame.

// ENTER FRAME HANDLER
private function enterFrameHandler(event:Event):void
{
	// draw perlin map
	drawPerlin();
 
	// increment z value used for perlin map
	_zValue += 0.04;
}

Her tegner vi vores Perlin noise og hæver z værdien.

Dette er stort set alt der skal til for at få lavet et animeret Perlin noise, den fulde kode som jeg bare har sat som document class i Flash CS4 kan ses her:

Kildekode

package 
{
	// IMPORTS
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.display.StageAlign;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.events.Event;
	import nl.ronvalstar.math.OptimizedPerlin;
 
	// CLASS
	public class PerlinAnim extends Sprite
	{
		//PRIVATE VARS
		private var _perlinData:BitmapData;
		private var _perlinHolder:Sprite;
		private var _zValue:Number = 1;
 
		// CONSTRUCTOR
		public function PerlinAnim()
		{
			// set stage scale and align
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
 
			// initialize
			init();
		}
 
		// INIT
		private function init():void
		{
			// create the perlin bitmaps etc.
			setupPerlin();
 
			// listen for stage resize and enter frame
			stage.addEventListener(Event.RESIZE, stageResizeHandler, false, 0, true);
			addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
		}
 
		// SETUP PERLIN
		private function setupPerlin():void
		{
			// settings for the perlin noise renderer
			// size of areas, integers, higher = smaller areas = more detailed
			OptimizedPerlin.octaves = 3;
			// smoothing, values: 0-1, lower = more smooth
			OptimizedPerlin.falloff = 0.5;
			// generate perlin seed(randomness)
			OptimizedPerlin.seed = Math.round(Math.random() * 123);
 
			// create bitmapdata that we will draw perlin map onto, keep size small for fast render
			_perlinData = new BitmapData(50,25,false,0x000000);
 
			// create bitmap for perlin bitmapdata, smoothing is important cause it will be scaled
			var perlinBitmap:Bitmap = new Bitmap(_perlinData,"auto",true);
 
			// setup holder and add to stage
			_perlinHolder = new Sprite();
			_perlinHolder.addChild(perlinBitmap);
			addChild(_perlinHolder);
 
			// scale perlin holder to stage size
			stageResizeHandler(null);
		}
 
		// DRAW PERLIN
		private function drawPerlin():void
		{
			// lock the bitmapdata (block updates)
			_perlinData.lock();
 
			// run through each pixel in the bitmapdata
			for (var i:Number = 0; i < _perlinData.width; ++i) 
			{
				for (var j:Number = 0; j < _perlinData.height; ++j) 
				{
					// get value from perlin noise (0-1)
					var noiseValue:Number = OptimizedPerlin.noise( i/_perlinData.width, j/_perlinData.height, _zValue );
					// convert value to color amount (0-255)
					var noiseColorValue:int = int( (.5+1.*(noiseValue-.5))*255 );
					// create blue color between full blue and full white
					var color:uint = noiseColorValue << 16 | noiseColorValue << 8 | 255;
					// set color on bitmapdata pixel
					_perlinData.setPixel(i, j, color);
				}
			}
 
			//unlock the bitmapdata
			_perlinData.unlock();
		}
 
		// ENTER FRAME HANDLER
		private function enterFrameHandler(event:Event):void
		{
			// draw perlin map
			drawPerlin();
 
			// increment z value used for perlin map
			_zValue += 0.04;
		}
 
		// STAGE RESIZE HANDLER
		private function stageResizeHandler(event:Event):void
		{
			// scale perlin holder to stage
			_perlinHolder.scaleX = stage.stageWidth/_perlinData.width;
			_perlinHolder.scaleY = stage.stageHeight/_perlinData.height;
		}
	}
}

Download

Kilde filerne kan downloades her:
http://flashforum.dk/files/artmos/AnimatedPerlinNoise.zip