Perlin Noise – 1 Dimensional
So I’ve been delving into Perlin Noise, looking for a way to generate lifelike textures procedurally. After researching online I hacked up a class to generate 1D Perlin noise… the 2D & 3D stuff will come soon hopefully. Here’s a demo of it, followed by the code.
Here’s the Perlin noise class at this point:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 package {
public class PerlinNoise {
public function PerlinNoise() {
/*call getPerlinNoise_1D*/
}
public function getPerlinNoise_1D(len:int=200, octaves:int=8, decay:Number=1):Array {
var graph:Array;
//totalGraph holds accumulated values
var totalGraph:Array = getNoiseFunctionResults(len, 1, 1);
var totalAmplitude:Number= 1;//used to normalize amplitude
for(var o:int = 1; o <= octaves; o++){
if(Math.pow(2,o) < len) {//cannot have frequency > len
graph = getNoiseFunctionResults(len, Math.pow(decay,o)/*amplitude*/, Math.pow(2,o)/*frequency*/);
totalAmplitude+=Math.pow(decay,o);
for(var g:int = 0; g < graph.length; g++){
//add the graphs together by shifting them so they are centred on y=0 (subtract their amplitude/2)
totalGraph[g] += graph[g];
}
}
}
//normalize values in totalGraph to be between -0.5 & 0.5 by dividing by accumulated amplitude
for(var i:int = 0; i < totalGraph.length; i++){
totalGraph[i] = totalGraph[i]/totalAmplitude;
}
return totalGraph;
}
private function getNoiseFunctionResults(len:int=200, amplitude:Number=1, freq:int=1):Array {
//trace("getNoise(amplitude="+amp+",frequency"+freq+")");
var result:Number = 0;//between -0.5 & 0.5
var wavelength:int = len/freq;//divide imgWidth by frequency to get wavelength
var results:Array = [];
//start with random seed
var seed:uint = Math.round(Math.random()*0x7FFFFFFF); //seed for prng... can be any uint (except 0)
//get next seed - used for interpolation
var nextseed:uint = prng(seed);
//for each x value
for(var x:int = 0; x < len; x++){
//if we are on a factor of wavelength ... get psuedorandom y value
if(x % wavelength == 0){
//store nextseed as seed
seed = nextseed;
//get next nextseed
nextseed = prng(seed);
//get y value from pseudorandom number
result = (seed/0x7FFFFFFF)*amplitude - amplitude/2;
} else {
//interpolate value between seed & nextseed
result = (interpolate(seed, nextseed, (x % wavelength)/wavelength)/0x7FFFFFFF)*amplitude - amplitude/2;
}
results.push(result);
}
return results;
}
private function prng(seed:uint):uint {
//to get a full period sequence you should feed back the seed
return seed * 16807 % 0x7FFFFFFF;
//to get a value between 0 & 1, divide result / 0x7FFFFFFF
}
private function interpolate(a:int,b:int,i:Number):Number {
//cosine interpolation
var ft:Number = i*Math.PI;
var f:Number = (1 - Math.cos(ft)) * .5;
return a*(1-f) + b*f;
}
}
}
// Copyright (c) 2008 David Wilhelm
// MIT license: http://www.opensource.org/licenses/mit-license.phpwww.opensource.org/licenses/mit-license.php
The code for the above demo of the class is here:
Note the use of Keith Peters’s excellent minimal comps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 package {
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.display.BitmapData;
import flash.text.TextField;
import flash.events.Event;
import com.bit101.components.*;
import flash.geom.Rectangle;</p>
[SWF(backgroundColor="0xFFFFFF", width="600", height="400", frameRate="30")]
public class PerlinNoise1D extends Sprite {
private const imgWidth:Number = 400;
private const imgHeight:Number = 400;
private var octaves:int = 8;
private var decay:Number= 1;
private var bm:Bitmap;
private var bmd:BitmapData;
public function PerlinNoise1D() {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
//create a bitmap data
bmd = new BitmapData(imgWidth, imgHeight, false, 0x00000000);
//create a bitmao using the data object
bm = new Bitmap(bmd);
addChild(bm);
drawGraph();
drawUI();
}
private function drawUI():void {
var ui:Sprite = new Sprite();
//octaves
var octaves_sldr:HSlider = new HSlider(ui, 50, 20, function(e:Event):void {
octaves = Math.round(e.target.value);
octaves_val.text = ""+octaves;
drawGraph();
});
octaves_sldr.setSliderParams(1,10,1);
var octaves_lbl:Label = new Label(ui, 0, 15, "octaves");
var octaves_val:Label = new Label(ui, 150, 15, "8");
//decay - between 0 & 1
var decay_sldr:HSlider = new HSlider(ui, 50, 40, function(e:Event):void {
decay = e.target.value;
decay_val.text = ""+decay;
drawGraph();
});
decay_sldr.setSliderParams(0,1,1);
var decay_lbl:Label = new Label(ui, 0, 35, "decay");
var decay_val:Label = new Label(ui, 150, 35, "1");
ui.x = 100;
ui.y = 5;
ui.alpha= 0.5;
addChild(ui);
}
private function drawGraph():void {
var y:Number = 0;
var perlin:PerlinNoise = new PerlinNoise();
bmd.fillRect(new Rectangle(0, 0, imgWidth, imgHeight), 0x00000000);
//get 1D Perlin noise
var pNoise:Array = perlin.getPerlinNoise_1D(imgWidth,octaves,decay);
trace(pNoise);
//loop through each point and draw
for(var i:int = 0; i < pNoise.length; i++){
y = pNoise[i]*imgHeight + imgHeight/2;//make y positive value between 0 & imgHeight
bmd.setPixel(i,Math.round(y), 0xFFFFFFFF);
}
//bm.bitmapData = bmd;
}
}
}
// Copyright (c) 2008 David Wilhelm
// MIT license: http://www.opensource.org/licenses/mit-license.php