Processingサンプルコードをわかりやすく解説【Noise1D】


お絵かきプログラミングProcessingサンプルコード解説の第15弾です!
ProcessingをまだPCに導入されていない方は、こちらの記事を参考に導入してみてください!
今回は Noise1D というサンプルコードを見てみましょう。この記事を読んでもらうと、↓このような「ふわふわと自然に動く」描画に必要なコードを理解することができます。

Noise1Dの実行結果

難易度は ★★☆☆☆ くらい。コードはとても短いですが、noise() という少し不思議な関数が登場します。今回はこの関数を中心に見ていきましょう!

注意!説明は Processing 4.5.7 で動作を確認したコードで行います。Mac版・Windows版でもほとんど同じものが現れると思います。
わからない箇所があればコメントをください。

とりあえず動かしてみよう

Processingを起動し、サンプルコードを開きましょう。
ファイル → サンプル と選択し現れたウィンドウで Basics → Math → Noise1D と選択してください。

このようなスケッチが現れます。ウィンドウの左上にある再生ボタンをクリックして実行してみましょう。

Noise1Dの実行結果

白い円が、左右にふわふわと動きます。動いた跡がうっすら残っているのもポイントですね。
今回のコードは パーリンノイズ(Perlin noise)を使った、なめらかなランダムな動き を扱います。

今回のコードはマウスやキーなどの外部入力は扱いません。

コードを読む

とりあえずコードの全貌を確認しましょう。

/**
 * Noise1D. 
 * 
 * Using 1D Perlin Noise to assign location. 
 */
 
float xoff = 0.0;
float xincrement = 0.01; 

void setup() {
  size(640, 360);
  background(0);
  noStroke();
}

void draw() {
  // Create an alpha blended background
  fill(0, 10);
  rect(0,0,width,height);
  
  //float n = random(0,width);  // Try this line instead of noise
  
  // Get a noise value based on xoff and scale it according to the window's width
  float n = noise(xoff)*width;
  
  // With each cycle, increment xoff
  xoff += xincrement;
  
  // Draw the ellipse at the value produced by perlin noise
  fill(200);
  ellipse(n,height/2, 64, 64);
}

短いですね!ではじっくり見ていきましょう。
まず初めのコメントを読みましょう。

/**
 * Noise1D. 
 * 
 * Using 1D Perlin Noise to assign location. 
 */

「1次元のパーリンノイズを使って位置を決める」とのことです。
パーリンノイズというのは、Ken Perlin さんが考案した「なめらかに変化するランダムな値」を作る方法です。Processingでは noise() という関数で簡単に使うことができます。

次にグローバル変数を見ましょう。

float xoff = 0.0;
float xincrement = 0.01; 
変数意味
xoffnoise() に渡す値。「ノイズのどの位置を読むか」
xincrement1フレームごとに xoff をどれだけ進めるか

xoff の off は offset(ずらす量)の略だと思われます。今はピンとこなくても大丈夫です。あとで noise() の仕組みと一緒に確認しましょう。

setup

void setup() {
  size(640, 360);
  background(0);
  noStroke();
}
  • size(): 描画ウィンドウのサイズを指定する関数。今回は 640×360 です。
  • background(): 画面全体を塗りつぶします。0 なので黒ですね。
  • noStroke(): 図形の枠線を描かないようにします。

よくみる関数ばかりで、シンプルですね。
では次に draw() を見てみましょう。

draw

void draw() {
  // Create an alpha blended background
  fill(0, 10);
  rect(0,0,width,height);
  
  //float n = random(0,width);  // Try this line instead of noise
  
  // Get a noise value based on xoff and scale it according to the window's width
  float n = noise(xoff)*width;
  
  // With each cycle, increment xoff
  xoff += xincrement;
  
  // Draw the ellipse at the value produced by perlin noise
  fill(200);
  ellipse(n,height/2, 64, 64);
}

少しずつ見ていきましょう。

  // Create an alpha blended background
  fill(0, 10);
  rect(0,0,width,height);

コメントには「アルファブレンドした背景を作る」とあります。
いつもなら background() で毎フレーム画面を塗りつぶすところですが、ここでは 半透明の黒い四角形を画面全体に重ねて います。
fill(0, 10) の2つ目の引数 10 は アルファ値(不透明度) で、0 が完全に透明、255 が完全に不透明です。
10 はかなり透明なので、前のフレームの円が一気には消えず、少しずつ暗くなっていきます。これが動いた跡(残像)の正体です。

次の行はコメントアウトされていますね。これはあとで使います。

  //float n = random(0,width);  // Try this line instead of noise

そして今回の主役です。

  // Get a noise value based on xoff and scale it according to the window's width
  float n = noise(xoff)*width;
  
  // With each cycle, increment xoff
  xoff += xincrement;

noise(xoff) は 0〜1 の値を返す関数 です。それに width(ウィンドウの幅 = 640)をかけているので、n は 0〜640 の値になります。つまり、円の x 座標として使える値ですね。
そして毎フレーム、xoff を xincrement(0.01)だけ増やしています。

最後は円を描いているだけです。

  // Draw the ellipse at the value produced by perlin noise
  fill(200);
  ellipse(n,height/2, 64, 64);

y 座標は height/2 で固定なので、円は画面の真ん中の高さを左右に動きます。
これで全部です。お疲れ様でした!

noise() と random() の違い

「ランダムな位置に円を描くなら random() でもいいのでは?」と思った方もいるかもしれません。
違いをグラフで見てみましょう。横軸を x、縦軸をそれぞれの関数が返した値にしてみました。

random()とnoise()の比較

上が random()、下が noise() です。一目瞭然ですね!
random() は毎回まったく関係のない値を返すのでギザギザですが、noise() は 引数が近ければ、返ってくる値も近い ので、なめらかな線になります。

noise() の性質をまとめるとこんな感じです。

  • 0〜1 の値を返す
  • 引数が少し変わると、値も少しだけ変わる(なめらか)
  • 同じ引数なら同じ値を返す(1回の実行の中では)
  • 実行するたびに模様は変わる(固定したい場合は noiseSeed() を使います)

3つ目の性質が大事です。noise(xoff) の xoff が変わらなければ、円はずっと同じ場所にいることになります。だから draw() の中で xoff を少しずつ進めているんですね。
xoff は「ノイズのグラフの上を、左から右へ読み進めていく位置」だとイメージするとわかりやすいと思います。

グラフの描画に使ったコードも載せておきます。

// random() と noise() の値をグラフにして比べる
void setup() {
  size(640, 360);
}

void draw() {
  background(255);
  textSize(16);

  // 上半分:random()
  stroke(220, 60, 60);
  noFill();
  beginShape();
  for (int x = 0; x < width; x++) {
    float y = random(0, 1) * 150 + 15;
    vertex(x, y);
  }
  endShape();

  // 下半分:noise()
  stroke(40, 90, 200);
  beginShape();
  for (int x = 0; x < width; x++) {
    float y = noise(x * 0.01) * 150 + 195;
    vertex(x, y);
  }
  endShape();

  // 区切り線とラベル
  stroke(180);
  line(0, height / 2, width, height / 2);
  fill(0);
  text("random()", 10, 20);
  text("noise(x * 0.01)", 10, height / 2 + 20);
  noLoop();
}

おまけ

Noise1D のサンプルについて理解を深めるための小さな問題を用意しました。興味がある方は取り組んでみてください。

問題1
21行目のコメントを外し、24行目をコメントアウトして、random() 版にすると円の動きはどうなるか確認してください。

問題2
xincrement の値を 0.001 や 0.1 に変えると、円の動きはどう変わるか確認してください。

問題3
円が左右だけでなく、上下にもふわふわ動くようにしてください。

解答例はこちらにおいておきます。

問題1の解答例

  float n = random(0,width);  // Try this line instead of noise
  
  // Get a noise value based on xoff and scale it according to the window's width
  //float n = noise(xoff)*width;
random()版の実行結果

円があちこちにワープするようになりました。random() は前の値と関係なく値を決めるので、こうなります。残像のおかげで、ちょっとにぎやかな絵になりますね。

問題2の解答例

xincrement を変えると、noise() の値の変化の仕方は次のようになります。

xincrementを変えたときの比較
  • 0.001:変化がとてもゆっくり。円はほとんど止まって見えます。
  • 0.01:元のサンプルの値。ふわふわした動きです。
  • 0.1:変化が激しく、ブルブル震えるような動きになります。

xincrement は「なめらかさ」を調整するつまみ だと覚えておくと便利です。

このグラフのコードはこちらです。

// xincrement(引数の進め方)を変えると、noise() の値の変化はどう変わる?
float[] increments = {0.001, 0.01, 0.1};

void setup() {
  size(640, 360);
}

void draw() {
  background(255);
  textSize(16);
  float h = height / 3.0;
  for (int i = 0; i < increments.length; i++) {
    float top = i * h;
    // 区切り線
    stroke(200);
    line(0, top, width, top);
    // グラフ
    stroke(40, 90, 200);
    noFill();
    beginShape();
    for (int x = 0; x < width; x++) {
      float n = noise(x * increments[i]);   // 0〜1 の値
      vertex(x, top + 10 + n * (h - 20));
    }
    endShape();
    fill(0);
    text("xincrement = " + increments[i], 10, top + 20);
  }
  noLoop();
}

問題3の解答例

/**
 * おまけ 問題3 の解答例
 * x と y の両方を noise() で動かす
 */

float xoff = 0.0;
float yoff = 1000.0;   // x とは離れた場所のノイズを使う
float xincrement = 0.01;

void setup() {
  size(640, 360);
  background(0);
  noStroke();
}

void draw() {
  fill(0, 10);
  rect(0, 0, width, height);

  float x = noise(xoff) * width;
  float y = noise(yoff) * height;

  xoff += xincrement;
  yoff += xincrement;

  fill(200);
  ellipse(x, y, 64, 64);
}
上下左右に動く円

ポイントは y 用のオフセット yoff を xoff とは離れた値(1000.0)から始めている ことです。
もし yoff も 0.0 から始めると、noise(xoff) と noise(yoff) は同じ値を返すので、円は斜めの直線の上しか動きません。ぜひ試してみてください。

さいごに

今回は、パーリンノイズを使った「なめらかなランダム」を学びました。
noise() は自然な動きや模様を作るのにとても便利で、ジェネラティブアート(プログラムで生成するアート)ではとてもよく使われる関数です。
次回は、この noise() を2次元に広げた Noise2D を見ていく予定です。
ではまた次回!

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。