Pages: [1]   Go Down
Author Topic: Perlin Noise Generator  (Read 3251 times)
0 Members and 1 Guest are viewing this topic.
Jersey City, NJ
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey all!

If you've worked with computer graphics, you've probably run across Perlin Noise before.  It's great for making clouds and other textures.  It's also a nice way to add the illusion of life to motion, etc. in a way that's a lot less random than, well, random().

I wrote the following Perlin Noise generator for the Arduino.  Now all the fun of Processing's noise() function is at your command on the chip!

Any additions, changes, corrections, etc. are highly appreciated.  And DEFINITELY let me know if you use this for something cool!

See:
http://freespace.virgin.net/hugo.elias/models/m_perlin.html

for the source of the algorithm I implemented and a great walkthrough of how to use it.

Code:
/*
copyright 2007 Mike Edwards

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
*/
#include <math.h>

float x1,y1,x2,y2,persistence;
int octaves;

void setup()
{
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  analogWrite(5,0);
  analogWrite(6,0);
  //persistence affects the degree to which the "finer" noise is seen
  persistence = 0.25;
  //octaves are the number of "layers" of noise that get computed
  octaves = 3;
}

void loop()
{
  x1 = float(millis())/100.0f;
  y1 = 10.0f;
  x2 = float(millis())/100.0f;
  y2 = 11.0f;

  //PerlinNoise2 results in a float between -1 and 1
  //below we convert to a n int between 0 and 255
  int m = int(PerlinNoise2(x1,y1,persistence,octaves)*128+128);  
  int n = int(PerlinNoise2(x2,y2,persistence,octaves)*128+128);
  
  analogWrite(5,m);
  analogWrite(6,n);
  delay(5);
}


//using the algorithm from http://freespace.virgin.net/hugo.elias/models/m_perlin.html
// thanks to hugo elias
float Noise2(float x, float y)
{
  long noise;
  noise = x + y * 57;
  noise = pow(noise << 13,noise);
  return ( 1.0 - ( long(noise * (noise * noise * 15731L + 789221L) + 1376312589L) & 0x7fffffff) / 1073741824.0);
}

float SmoothNoise2(float x, float y)
{
  float corners, sides, center;
  corners = ( Noise2(x-1, y-1)+Noise2(x+1, y-1)+Noise2(x-1, y+1)+Noise2(x+1, y+1) ) / 16;
  sides   = ( Noise2(x-1, y)  +Noise2(x+1, y)  +Noise2(x, y-1)  +Noise2(x, y+1) ) /  8;
  center  =  Noise2(x, y) / 4;
  return (corners + sides + center);
}

float InterpolatedNoise2(float x, float y)
{
  float v1,v2,v3,v4,i1,i2,fractionX,fractionY;
  long longX,longY;

  longX = long(x);
  fractionX = x - longX;

  longY = long(y);
  fractionY = y - longY;

  v1 = SmoothNoise2(longX, longY);
  v2 = SmoothNoise2(longX + 1, longY);
  v3 = SmoothNoise2(longX, longY + 1);
  v4 = SmoothNoise2(longX + 1, longY + 1);

  i1 = Interpolate(v1 , v2 , fractionX);
  i2 = Interpolate(v3 , v4 , fractionX);

  return(Interpolate(i1 , i2 , fractionY));
}

float Interpolate(float a, float b, float x)
{
  //cosine interpolations
  return(CosineInterpolate(a, b, x));
}

float LinearInterpolate(float a, float b, float x)
{
  return(a*(1-x) + b*x);
}

float CosineInterpolate(float a, float b, float x)
{
  float ft = x * 3.1415927;
  float f = (1 - cos(ft)) * .5;

  return(a*(1-f) + b*f);
}

float PerlinNoise2(float x, float y, float persistance, int octaves)
{
  float frequency, amplitude;
  float total = 0.0;

  for (int i = 0; i <= octaves - 1; i++)
  {
    frequency = pow(2,i);
    amplitude = pow(persistence,i);

    total = total + InterpolatedNoise2(x * frequency, y * frequency) * amplitude;
  }

  return(total);
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 16
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Funny, I did that too, just weeks ago, its running fine for days and produces great curves... This (Kens) one could some time be part of gnu math.h, couldn't it? Somehow I cant get the formatting ok, does the board software unexpand blanks?

Code:
#include <math.h>

/*
 Ken Perlins improved noise   -  http://mrl.nyu.edu/~perlin/noise/
 C-port:  http://www.fundza.com/c4serious/noise/perlin/perlin.html
 by Malcolm Kesson;   arduino port by Peter Chiochetti, Sep 2007 :
 -  make permutation constant byte, obsoletes init(), lookup % 256
*/

static const byte p[] = {   151,160,137,91,90, 15,131, 13,201,95,96,
53,194,233, 7,225,140,36,103,30,69,142, 8,99,37,240,21,10,23,190, 6,
148,247,120,234,75, 0,26,197,62,94,252,219,203,117, 35,11,32,57,177,
33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,
48,27,166, 77,146,158,231,83,111,229,122, 60,211,133,230,220,105,92,
41,55,46,245,40,244,102,143,54,65,25,63,161, 1,216,80,73,209,76,132,
187,208, 89, 18,169,200,196,135,130,116,188,159, 86,164,100,109,198,
173,186, 3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,
212,207,206, 59,227, 47,16,58,17,182,189, 28,42,223,183,170,213,119,
248,152,2,44,154,163,70,221,153,101,155,167,43,172, 9,129,22,39,253,
19,98,108,110,79,113,224,232,178,185,112,104,218,246, 97,228,251,34,
242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,
150,254,138,236,205, 93,222,114, 67,29,24, 72,243,141,128,195,78,66,
215,61,156,180
};

double fade(double t){ return t * t * t * (t * (t * 6 - 15) + 10); }
double lerp(double t, double a, double b){ return a + t * (b - a); }
double grad(int hash, double x, double y, double z)
{
int     h = hash & 15;          /* CONVERT LO 4 BITS OF HASH CODE */
double  u = h < 8 ? x : y,      /* INTO 12 GRADIENT DIRECTIONS.   */
          v = h < 4 ? y : h==12||h==14 ? x : z;
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}

#define P(x) p[(x) & 255]

double pnoise(double x, double y, double z)
{
int   X = (int)floor(x) & 255,             /* FIND UNIT CUBE THAT */
      Y = (int)floor(y) & 255,             /* CONTAINS POINT.     */
      Z = (int)floor(z) & 255;
x -= floor(x);                             /* FIND RELATIVE X,Y,Z */
y -= floor(y);                             /* OF POINT IN CUBE.   */
z -= floor(z);
double  u = fade(x),                       /* COMPUTE FADE CURVES */
        v = fade(y),                       /* FOR EACH OF X,Y,Z.  */
        w = fade(z);
int  A = P(X)+Y,
     AA = P(A)+Z,
     AB = P(A+1)+Z,                        /* HASH COORDINATES OF */
     B = P(X+1)+Y,
     BA = P(B)+Z,
     BB = P(B+1)+Z;                        /* THE 8 CUBE CORNERS, */

return lerp(w,lerp(v,lerp(u, grad(P(AA  ), x, y, z),   /* AND ADD */
                          grad(P(BA  ), x-1, y, z)),   /* BLENDED */
              lerp(u, grad(P(AB  ), x, y-1, z),        /* RESULTS */
                   grad(P(BB  ), x-1, y-1, z))),       /* FROM  8 */
            lerp(v, lerp(u, grad(P(AA+1), x, y, z-1),  /* CORNERS */
                 grad(P(BA+1), x-1, y, z-1)),          /* OF CUBE */
              lerp(u, grad(P(AB+1), x, y-1, z-1),
                   grad(P(BB+1), x-1, y-1, z-1))));
}

#define ledPin 13

void blink(int dur)
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  delay(dur);
  digitalWrite(ledPin, LOW);
}

void setup()
{
  Serial.begin(19200);
  blink(1000);
}

/* Koordinatenpaare rechnen */
void loop()
{
  double x, y;
  static double i = 0;

  blink(125);

  i += 0.01;
  // aktuelle Iteration ausgeben
  Serial.print("# ");
  Serial.println((long)(i*100));

  // nächsten Punkt berechnen
  x = pnoise(i+sin(i), i+cos(i), i);
  y = pnoise(i-sin(i), i-cos(i), i);

  // aktuelle Koordinaten ausgeben
  Serial.print(x);
  Serial.print(" ");
  Serial.println(y);
}
Logged

Pages: [1]   Go Up
Jump to: