Il rumore di Perlin: casualità organica

Il rumore di Perlin è un effetto visuale che produce una casualità organica utile per simulare elementi naturali. E' stato sviluppato da Ken Perlin lavorando per il film Tron (1982) di Disney. Nel 1997, Perlin vinse un Academy Award for Technical Achievement (Oscar) da parte della Academy of Motion Picture Arts and Sciences per il suo contributo all'arte cinematografica.

Vediamo un esempio incrementale. Lo sketch Keplero1 disegna una ellisse punto dopo punto.

/* 
* Disegna una ellisse
*/
float a; // semiasse maggiore dell'ellisse
float b; // semiasse minore dell'ellisse
float alpha; // angolo
float lastx, lasty, x, y; // coordinate precedenti e correnti

void setup() {
  size(600, 400); 
  background(255);
  smooth(); 
  stroke(20, 50, 70);
  strokeWeight(5);
  a = 200;
  b = 100;
  // punto iniziale
  alpha = 0;
  x = width/2 + a;
  y = height/2;
}


void draw() {
  lastx = x;
  lasty = y;  

  // calcolo le coordinate di un punto sull'ellisse
  x = width/2  + a * cos(alpha);
  y = height/2 + b * sin(alpha);

  // unisco con una linea il punto corrente e quello precedente
  line(x, y, lastx, lasty);

  // incremento l'angolo di un grado
  alpha = alpha + radians(1); 
}

Non è difficile passare da una ellisse ad una spirale ellittica (Keplero2).

/* 
* Disegna una spirale ellittica
*/
float a; // semiasse maggiore dell'ellisse
float b; // semiasse minore dell'ellisse
float alpha; // angolo
float lastx, lasty, x, y; // coordinate precedenti e correnti
float stepa, stepb; // incrementi degli assi


void setup() {
  size(600, 400); 
  background(255);
  smooth(); 
  stroke(20, 50, 70);
  strokeWeight(5);
  a = 0;
  b = 0;
  stepa = 0.2;
  stepb = 0.1;
  // punto iniziale
  alpha = 0;
  x = width/2;
  y = height/2;
}


void draw() {
  lastx = x;
  lasty = y;  

  // calcolo le coordinate di un punto sull'ellisse
  x = width/2  + a * cos(alpha);
  y = height/2 + b * sin(alpha);

  // unisco con una linea il punto corrente e quello precedente
  line(x, y, lastx, lasty);

  // incremento l'angolo di un grado
  alpha = alpha + radians(1); 
  
  // incremento gli assi dell'ellisse
  a = a + stepa;
  b = b + stepb;

  
}

Usando la variabilità organica ci muoviamo nella direzione del caos e cediamo parte del controllo alla macchina (Keplero3).

/* 
* Disegna una spirale ellittica mostrando la differenza tra casualità e rumore di Perlin
*/
float a; // semiasse maggiore dell'ellisse
float b; // semiasse minore dell'ellisse
float alpha; // angolo
float stepa, stepb; // incrementi degli assi
float lastx, lasty, x, y; // coordinate precedenti e correnti
float noi; // rumore di Perlin 
float variance; // varianza degli assi

void setup() {
  size(600, 400); 
  background(255);
  smooth(); 
  stroke(20, 50, 70);
  strokeWeight(5);
  a = 0;
  b = 0;
  stepa = 0.2;
  stepb = 0.1;
  // punto iniziale
  alpha = 0;
  x = width/2;
  y = height/2;
  // seme iniziale scelto in modo casuale
  noi = random(10);
  variance = 200;
}


void draw() {
  lastx = x;
  lasty = y;  
  float mya, myb;
  
  // rumore
  mya = a + (noise(noi) - 0.5) * variance;
  myb = b + (noise(noi) - 0.5) * variance;
  
  // casualità
  //mya = a + (random(1) - 0.5) * variance;
  //myb = b + (random(1) - 0.5) * variance;

  // calcolo le coordinate di un punto sull'ellisse
  x = width/2 +  mya * cos(alpha);
  y = height/2 + myb * sin(alpha);

  // unisco con una linea il punto corrente e quello precedente
  line(x, y, lastx, lasty);

  // incremento l'angolo
  alpha = alpha + radians(1); 
  
  // incremento gli assi dell'ellisse
  a = a + stepa;
  b = b + stepb;
  
  // incremento il seme del rumore di Perlin
  noi = noi + 0.05;
  
}

Moltiplichiamo per 100 e aggiungiamo un po' di casualità (Keplero4).

/* 
* Disegna 100 spirali ellittiche 
*/
float a; // semiasse maggiore dell'ellisse
float b; // semiasse minore dell'ellisse
float stepa, stepb; // incrementi degli assi
float lastx, lasty, x, y; // coordinate precedenti e correnti
float noi; // rumore di Perlin 
float variance; // varianza degli assi
float startAngle; // angolo iniziale
float endAngle;   // angolo finale
float stepAngle;  // incremento dell'angolo

void setup() {
  size(500, 500); 
  background(255);
  smooth(); 


  variance = 200;
  stepa = 0.5;
  stepb = 0.5;
  for (int i=0; i < 100; i++) {
    a = 10;
    b = 10;
    strokeWeight(random(2));
    noi = random(10);
    color col = color(random(200), random(200), random(200));
    float trasp = random(200);
    stroke(col, trasp);
  
    startAngle = radians(random(360));
    endAngle = radians(360 * 4 + random(360 * 4));
    stepAngle = radians(random(3, 8));

    x = width/2  + a * cos(startAngle);
    y = height/2 + b * sin(startAngle);

    drawSpiral();
  }
}

void drawSpiral() {

  float mya, myb;
  
  for (float alpha = startAngle; alpha < endAngle; alpha += stepAngle) {
    
    lastx = x;
    lasty = y;  

    mya = a + (noise(noi) - 0.5) * variance;
    myb = b + (noise(noi) - 0.5) * variance;
    
    // calcolo le coordinate di un punto sull'ellisse
    x = width/2 +  mya * cos(alpha);
    y = height/2 + myb * sin(alpha);
  
    // unisco con una linea il punto corrente e quello precedente
    line(x, y, lastx, lasty);
  
    // incremento gli assi dell'ellisse
    a = a + stepa;
    b = b + stepb;
    
    // incremento il seme del rumore di Perlin
    noi = noi + 0.05;
  }
}

void draw() {}

void mouseReleased() {
  save("Keplero.gif");
}  

Qualcosa di più bello che usa il rumore di Perlin per decostruire una sfera tridimensionale: Frosti (2010) di Matt Pearson.