Processing/p5.js の noise() で遊ぼう! 前編
クリエイティブ・コーディングでよく使われるノイズですが、とっつきにくいとか、いまいち使い方がよくわからないという声も聞かれるようです。
今回、簡単な作例を交えながら noise() の特徴や使い方をご紹介したいと思います。noise() をマスターすれば作品作りがグッと面白くなるかも!
「ノイズ」にはいろいろな種類がありますが、ここでは Processing/p5.js の noise() 関数(パーリンノイズの Processing/p5.js での実装)を取り上げます。
読みやすさと実践的な理解を優先して、「正確にはそうとは言い切れないんだけど」とか「場合によっては例外があって」などはバッサリ省きました。
サンプルコードは Processing のものを掲載し、p5.js のコードは OpenProcessing へのリンクで示してあります。
掲載のコードは CC0 で公開します。ご自由に利用ください。
この記事は全文無料でお読みいただけます。もしお気に召しましたら投げ銭お願いしますね。😉✨
noise() の特徴1:なめらかに変化するランダム
noise() を一言で表現すると「なめらかに変化するランダム」です。
random() は皆さん使われたことがあると思います。
float y = random(height);
でグラフを描くとこんな感じになりますね。
これが noise() だとこうなります。
なんて滑らか!😀
色の変化に適用してみると、random() だとこんな感じ。
バラバラですね。
これが noise() だとこう!
色のグラデーション処理を入れてるわけじゃないですよ。単に random() を noise() に変えただけです。
このように noise() を使うと滑らかに変化するランダム値を得ることができます。
先程のグラフを描くコードを見てみましょう。
/**
* noise() で遊ぼう! 前編
* random/noise グラフ
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* Processing 3.5.3
* 2020.10.31
*/
void setup() {
size(640, 480);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
noLoop();
background(0.0, 0.0, 90.0, 100.0);
noFill();
stroke(0.0, 0.0, 0.0, 100.0);
strokeWeight(2.0);
}
void draw() {
int div = 50;
beginShape();
for (int i = 0; i < div; i++) {
float x = i * width / div;
// float y = random(height);
float y = noise(i * 0.05) * height;
vertex(x, y);
ellipse(x, y, 10.0, 10.0);
}
endShape();
}
肝心なところはここです。(random() の方がコメント化されてます)
// float y = random(height);
float y = noise(i * 0.05) * height;
random(height) はわかりますよね。0 から height の手前までの値をランダムに返します。
対して noise(i * 0.05) は、0 から 1 の間の値をランダムに返します。
❓❓❓ i * 0.05 って何 ❓❓❓
ですよね。 noise() はカッコ内の引数が何であろうと 0 から 1 の間の値を返します。
試しに random(height) と同じように
float y = noise(height);
としてみましょうか?結果はこうです。
0 から 1 の間の値を返すんだからこうなりますわな。🤷♀️
じゃあせめて 100倍して 0 から 100 の間の値にしてみましょう。
float y = noise(height) * 100.0;
こうなりました。なんだか線が真っすぐで上下してないですね?
そうなんです。 noise() は
・カッコ内の引数が何であろうと 0 から 1 の間の値を返す
の他に
・カッコ内の引数の値が同じなら結果も同じ値を返す
という性質を持っています。
ではカッコ内の引数を変えて
float y = noise(i * 0.05) * 100.0;
で実行してみると…
カッコ内の引数の値が変化することで、結果も変化して線が上下するようになりました。
同じコードをもう一回実行してみましょう。
あれっ?さっきと違う結果になりましたよ!? 😲
そうなんです。「カッコ内の引数の値が同じなら結果も同じ」と言いましたが、
・ただし、それは Processing/p5.js の一回の動作の中で
という条件付きです。
引数の値が同じでも返ってくる結果は実行の度に異なるものになります。
❓❓❓ 「滑らか」ってどれぐらい ❓❓❓
これを見ると左から右に移動するにつれて上下に滑らかに変化してますよね? コードで言うと i が大きくなるにつれて y の値が滑らかに変化していきます。
つまり、noise() は
・カッコ内の引数の値の変化に伴って結果が滑らかに変わる
ということです。
でも「滑らか」って言ってもどれぐらい滑らかなんでしょう?
それは、カッコ内の引数の値の変化の度合いによります。
例えばさっきの
float y = noise(i * 0.05) * 100.0;
の変化を 1/10倍にしてみましょう。
float y = noise(i * 0.005) * 100.0;
滑らか過ぎる!変化してんのかなこれ? 🤔
逆に10倍だと?
float y = noise(i * 0.5) * 100.0;
粗い!まるで random()!
と、引数の変化具合によって得られる滑らかさが大きく変わってきます。
目的によっても違いますが、最初は 0.05 から 0.1 の変化率で試してみて結果を見ながら調整するといいと思います。
ここまで noise() の性質をまとめるとこうなります。
・カッコ内の引数の値の変化に伴って結果が滑らかに変わる
・滑らかさは引数の値の変化の度合いによる
・引数が何であろうと 0 から 1 の間の値を返す
・引数の値が同じなら結果も同じ値を返す(ただし、一回の動作の中で)
noise() の特徴2:引数の絶対値が同じなら結果は同じ
最後の「引数の値が同じなら結果も同じ値を返す」ですが、実は引数の値のプラスとマイナスを逆にしても同じ結果を返します。
つまり、最後の性質は
・引数の絶対値が同じなら結果も同じ値を返す(ただし、一回の動作の中で)
となります。
この性質を利用してこんな風に noise() で上下左右対称の図を描くことができます。
/**
* noise() で遊ぼう! 前編
* 引数の絶対値で結果は同じ
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* Processing 3.5.3
* 2020.10.31
*/
void setup() {
size(640, 480);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
noLoop();
background(0.0, 0.0, 90.0, 100.0);
stroke(0.0, 0.0, 0.0, 100.0);
strokeWeight(1.0);
}
void draw() {
int div = 50;
float w = width * 0.5 / div;
float h = height * 0.5 / div;
translate(width * 0.5, height * 0.5);
for (int i = -div; i < div; i++) {
for (int j = -div; j < div; j++) {
float x = i * w;
float y = j * h;
fill(noise(i * j * 0.01) * 360.0, 80.0, 90.0, 100.0);
rect(x, y, w, h);
}
}
}
ひと工夫するとこんな風にも。
noise() の特徴3:引数の次元
これは特徴というか使い方になりますね。
Processing/p5.js のリファレンスを見ると、noise() のカッコの中に入れる引数は 1個から 3個となっています。
引数 1個の場合 1次元ノイズ、2個なら 2次元ノイズ、3個は 3次元ノイズと呼んだりします。
noise(x) // 1次元ノイズ
noise(x, y) // 2次元ノイズ
noise(x, y, z) // 3次元ノイズ
ここまで使ってきたのは 1次元ノイズでした。2次元ノイズ、3次元ノイズでもその特徴は同じです。
・カッコ内の引数の値の変化に伴って結果が滑らかに変わる
・滑らかさは引数の値の変化の度合いによる
・引数が何であろうと 0 から 1 の間の値を返す
・引数の絶対値が同じなら結果も同じ値を返す(ただし、一回の動作の中で)
ここで 2次元ノイズを使ってこういうコードを書いてみます。
/**
* noise() で遊ぼう! 前編
* グラフで見てみる 2次元ノイズ
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* Processing 3.5.3
* 2020.10.31
*/
void setup() {
size(640, 320);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
noLoop();
background(0.0, 0.0, 90.0, 100.0);
noFill();
stroke(0.0, 0.0, 0.0, 100.0);
strokeWeight(2.0);
}
void draw() {
int divX = 50;
int divY = 5;
float w = width / divX;
float h = height / divY;
for (int j = 0; j < divY; j++) {
beginShape();
for (int i = 0; i < divX; i++) {
float x = i * w;
float y = noise(i, j) * 100 + j * h;
vertex(x, y);
}
endShape();
}
}
ノイズを使ってるのはここです。
float y = noise(i, j) * 100 + j * h;
結果はこのようになります。
だいぶ粗いですね。ここで横方向の変化度合いだけを小さくしてみます。
float y = noise(i * 0.1, j) * 100 + j * h;
横方向が滑らかになりましたね。ここまでは今までどおり。
では縦方向の変化度合いを小さくしたらどうなるでしょう? 横方向は元に戻して、縦方向だけ変化度合いを小さくしてみます。
float y = noise(i, j * 0.1) * 100 + j * h;
どうです?それぞれの線の形がなんだか似てませんか?
そうです!縦方向の変化度合いを小さくしたので、それぞれの線の違いがちょっとずつになったのです!
試しに縦方向を全く変化しないようにしてみると?
float y = noise(i, j * 0.0) * 100 + j * h;
横方向の変化だけになって、全ての線が同じ形になりました。つまり 1次元ノイズと同じことになったわけです。
2次元ノイズで静止画
別のコードでもう少し2次元ノイズを見ていきましょう。
/**
* noise() で遊ぼう! 前編
* いかにも 2次元ノイズな図
*
* @author @deconbatch
* @version 0.1
* @license CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/deed.ja
* Processing 3.5.3
* 2020.10.31
*/
void setup() {
size(640, 480);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
noLoop();
background(0.0, 0.0, 90.0, 100.0);
fill(220.0, 90.0, 60.0, 100.0);
stroke(40.0, 60.0, 90.0, 100.0);
strokeWeight(5.0);
}
void draw() {
int divX = 16;
int divY = 12;
float w = width / divX;
float h = height / divY;
for (int i = 0; i < divX; i++) {
float x = i * w;
for (int j = 0; j < divY; j++) {
float y = j * h;
float s = noise(x * 0.005, y * 0.005);
rect(x, y, s * w, s * h);
}
}
}
2次元ノイズを四角形の大きさに反映させました。横方向も縦方向も大きさが滑らかに変化しているでしょう?
つまり、
noise(x * 0.005, y * 0.005)
の x が同じなら y の変化(縦方向)にそって滑らかに変化、
y が同じなら x の変化(横方向)にそって滑らかに変化しているわけです。
縦方向が滑らかに変化してるから上下の隣り合った行同士はちょっとずつ違ったものになっているし、
同様に左右の隣り合った列同士もちょっとずつ違ったものになっています。
その結果、どの四角形を見てみても上下左右斜めに位置する四角形はちょっとだけ違う大きさになっています。
つまり 2次元ノイズは 2つの引数それぞれを縦横と考えた時、縦横斜めどちらに移動してもちょっとずつ変化するランダムな値を取れるのです。
これが 2次元ノイズのイメージです!
色にもノイズを反映させてみるとこうなります。
マス目を細かくして、色と鮮やかさと明るさにそれぞれ異なるノイズを適用してみるとこういうものが出来ます。
なんか見たことある感じのやつでしょ?
見たことある感じと言えばこんなのも。
通称「スネ毛」です。
ぐっと細かくすると「スネ毛の地図」になります。(そんなものはない 😆)
これのそれぞれの線を 2次元ノイズでずっと繋げていくとノイズ・フィールドとかベクター・フィールドと呼ばれるものが出来ます。
noise() 使ってみよう!
じゃあ noise() を使って何か作ってみましょう!
2次元ノイズで静止画なんて楽しそうです。
線だったり、円だったり、四角形だったり。大きさ、方向、色などノイズで変化させられるものはいろいろありますね。
いちから作ってもいいし、掲載のサンプルコードをいじるところから始めてもいいでしょう。
スネ毛の地図を再現してみるというのもチャレンジングでいいかも!😆
ノイズを使うのが全くの初めてでしたら、1次元ノイズでグラフを描くことから始めるのもいいと思います。
同じコードでも noise() 引数の変化率を変えるだけでかなり雰囲気変わるのでいろいろ試してみるのも面白いですよ。
例えばこれ、線グラフを縦にして山ほど描いてみた作例です。
こちらの Twitter のスレッドでも noise() での作例をいくつか公開しています。もしよろしければご参考までに。
ぜひお楽しみあれ!
後編ではノイズを使ってアニメーションを作ります。こちらもお楽しみに!😀
こんなの作っていただきました。
センバク(@senbaku)さんの作品です。ノイズで画数が変わるって発想が新しいです!🤩
takawo shunsuke(@takawo)さんの作例。
if() 文によってノイズの変化の結果をパキッと 2つに振り分けてます。しかも描画は DOM の checkbox! 処理内容と描画方法がぴったりマッチ!😀👍
nagayama(@nagayama)さんの作品です。一次元ノイズで動く曲線を「つぶやき Processing」で描いてらっしゃいます! 😲
takawo shunsuke(@takawo)さんのノイズ・フィールドの作例です。p5.Vector の使い方がスマートでカッコいい!😍
Naoto HIÉDA(@naoto_hieda)さんの作品。hydra-synth.js の noise() で色に変化を付ける作例のようです。
これは Processing/p5.js の noise() とはまた違う実装のノイズだそうです。🤔
はぅ君(@Hau_kun)さんの作品。記事中で紹介した「スネ毛」を「つぶやき Processing」で描いてくださってます。「つぶやき」の場合でもなるべくコードの可読性を保ったままにしようとされてるそうで、今回もコード読みやすいですね。😊
wks(@wks_jp)さんのさわやかな作例!色調と描画の大きさに2次元ノイズを当てて綺麗なモコモコ感出してらっしゃいます。😊
ねじおさん(@akane45_gun)の作例です。色と形の変化からリズムが感じられるようで面白いですね!😀
ボツと仰っしゃりながら、こちらもイイ!👍
eboshidori(@_eboshidori)さんの作例です。こんな風に円状にドットを分布させるのって以外と難しいんですよね。陰影も出ていて、白い小さなドットの帯状部分がシブい!😎
この先には noise() を使用したちょっと大きめの作品画像が 2枚あるだけです。この記事がお気に召しましたら投げ銭お願いしますね。😉✨
ここから先は
¥ 100
この記事が面白かったらサポートしていただけませんか? ぜんざい好きな私に、ぜんざいをお腹いっぱい食べさせてほしい。あなたのことを想いながら食べるから、ぜんざいサポートお願いね 💕