第7章 周期と偶然のあいだ
機械的に同じ速さで動く円は、動きの仕組みを知るには分かりやすい例です。しかし呼吸、波、風のような動きは、行って戻る変化や、完全には予測できない揺れを含みます。周期と乱数は、その 2 種類の変化を作ります。
7.1 sin が往復運動を返す
sin(サイン)は、三角関数と呼ばれる計算の一つです。この本では、公式も計算方法も覚えません。数字を少しずつ増やして sin へ渡すと、答えが -1 から 1 の間をなめらかに行き来することだけを、画面の動きに使います。-1 は「マイナス 1」と読み、0 より小さい数です。
def setup
createCanvas(500, 240)
end
def draw
background(245)
wave = sin(frameCount * 0.04)
x = width / 2 + wave * 180
fill(235, 90, 120)
ellipse(x, height / 2, 60, 60)
end
frameCount(フレーム・カウント)は、draw が何回呼ばれたかを表す値です。1、2、3 と増えていく番号へ 0.04 を掛け、sin へ渡します。戻ってくる wave は -1 から 1 の間なので、180 を掛けると -180 から 180 の範囲になります。
まず 0.04 を 0.02 へ変えて実行してみましょう。動きが遅くなれば、数字と速さの関係が画面に現れています。次に元へ戻し、180 を 80 へ変えると、左右へ動く幅が小さくなります。
0.04 を小さくするとゆっくり、大きくすると速く往復します。180 は移動幅です。速度と振れ幅を別の数字で調整できる点が重要です。
7.2 同じ周期を大きさへ使う
同じ wave を位置ではなく大きさへ使います。次の変更部分を、7.1 の長いコードにある draw の中身と置き換えます。
background(245)
wave = sin(frameCount * 0.04)
size = 100 + wave * 40
fill(235, 90, 120)
ellipse(width / 2, height / 2, size, size)
中央の赤い円が、大きくなったり小さくなったりします。background(245) が前の円を消すため、今の大きさだけが見えます。大きさは 60 から 140 の間を往復します。wave をそのまま大きさにすると負の値になる時間があります。中心となる 100 を足すことで、扱いやすい範囲へ移しています。
まず 40 を 20 へ変えて実行し、大きさの変化が小さくなるか見ます。元へ戻すときは 40 に戻します。エラーになったときは、draw の中身を上の 5 行へ戻して実行します。
周期の式は、横移動、透明度、色、回転にも使えます。同じ wave を位置と大きさの両方に使うと、二つが同じ速さで繰り返し変化します。別の速さを使うと、変化する時期が少しずつずれます。
7.3 random は毎回別の値を選ぶ
random(400) は、0 以上 400 未満の範囲から値を返します。draw のたびに位置を選ぶと、円が激しく飛びます。
def setup
createCanvas(500, 300)
background(20)
noStroke
end
def draw
x = random(width)
y = random(height)
size = random(4, 28)
fill(255, 220, 120, 120)
ellipse(x, y, size, size)
end
背景を setup に置いたため、点が消えずに積もります。実行するたび別の星空になります。
位置、色、大きさをすべて乱数にすると、どこを見る画面なのか分かりにくくなることがあります。たとえば大きさを揃え、位置だけを乱数にすると、二つの役割を分けて調整できます。
7.4 noise は隣り合う値がつながる
random(ランダム)は呼ぶたび大きく飛べます。noise は、近い入力へ近い値を返すため、連続した揺れを作りやすい機能です。
def setup
createCanvas(500, 240)
end
def draw
background(245)
value = noise(frameCount * 0.01)
x = value * width
fill(60, 130, 220)
ellipse(x, height / 2, 60, 60)
end
noise(ノイズ)の戻り値は通常 0 から 1 の範囲です。width(ウィズ、キャンバスの幅)を掛けると、キャンバスの左端から右端の範囲へ変換できます。
二つの変化を横へ並べる代わりに、上段と下段へ点として記録すると形を比べられます。次はそれだけで実行できる完成コードです。
def setup
createCanvas(600, 300)
background(245)
noStroke
120.times do |i|
x = 30 + i * 4.5
wave_y = 80 + sin(i * 0.18) * 35
fill(235, 90, 120)
ellipse(x, wave_y, 6, 6)
noise_y = 220 + (noise(i * 0.08) - 0.5) * 90
fill(50, 130, 230)
ellipse(x, noise_y, 6, 6)
end
end
上段の赤い点は、同じ波の形を繰り返します。下段の青い点は滑らかにつながっていますが、次の山や谷の大きさは一定ではありません。
7.5 偶然を保存する
乱数を使った作品で気に入った結果が出たら、その画面を保存する方法があります。再現できることも重要なら、randomSeed(ランダム・シード)で乱数の並びを固定できます。
def setup
createCanvas(500, 300)
randomSeed(42)
background(20)
100.times do
fill(255, 220, 120, 140)
ellipse(random(width), random(height), random(4, 24), random(4, 24))
end
end
同じシード値 42 なら、同じ乱数列を得られます。偶然に見える構図を選び、あとから同じ版を作れるようになります。
7.6 波と偶然を同じ海へ置く
sin で規則的に上下する線を作り、noise で少しだけ位置を揺らすと、完全な規則でも完全な乱数でもない水面を作れます。さらに random で小さな光を散らすと、波、風、反射という別々の役割を同じ画面へ置けます。
ここでは完成コードを一つに決めません。星空のコードを青い背景へ変えて水面にする、往復する円を船に見立てて三角形を足す、noise の動きを雲へ使う、のどれかから始めます。同じ数の変化が、何に見立てるかで別作品になることを試す節です。
7.7 模様をもっと探すなら
第6章の行数と列数、第7章の sin、noise、random を組み合わせると、同じコードから規則的な模様と崩れた模様を作り分けられます。まず一つだけ選びます。
- 行と列の数を増やし、遠くから見た印象を比べる
sinを位置や大きさへ使い、並びを波打たせるnoiseを色へ使い、少しずつ変わる帯を作る- 第6章のフラクタルで、円を四角や枝へ変える
さらに探したくなったら、「セル・オートマトン」「フラクタル」「ジェネラティブアート」という言葉を、画像やp5.js の作品例から調べられます。Genuary では、毎年 1 月にクリエイティブコーディングの制作テーマが公開されます。見つけた作品を全部まねるのではなく、気になった形の並び方や動き方を一つ選び、自分のスケッチで短く試します。
第3章で ellipse を探したときと同じように、rbCanvas/p5 0.5.1 API リファレンスで sin、noise、random のどれか一つを探せます。まず掲載されたサンプルを別のタブで開いたエディタで実行し、数字を一つだけ変えます。本書のコードと見比べると、その数字が速さ、揺れ方、値の範囲のどれに関係するかを調べられます。
どの動きをもう一度見たいか決め、その数字をメモします。第3部では、マウスとキーボードから受け取った値で画面を変えます。
参考資料
- rbCanvas/p5 0.5.1 API リファレンス —
sin、noise、random、randomSeedの説明 - p5.js
noise— 近い入力から滑らかに変化する値を得る考え方。サンプルは JavaScript で書かれている - p5.js
randomSeed— 乱数列を再現するためのシード