p5.jsでGenerative Artを作る
はじめに
数学的な計算やアルゴリズムを利用したアートの分野 "Generative Art" というもの存在することを最近知ったので、 自分で開発環境を設定し、実際にGenerative Art画像を作成してみました。
環境
開発環境はProcessingがメジャーのようですが、Processingを翻訳してwebで使用できるようにしたものがp5.jsとして存在し、 Javascriptを使用して開発できるようなのでp5.jsを採用しました。 ただp5標準のエディターは使用せずindex.htmlからp5.jsを読み込んで、使い慣れているVSCodeからsketch.jsの編集を行うようにしました。 https://p5js.org/download/ からComplete Libraryをダウンロードすればすぐ使用可能です。
p5のReferenceを確認するとp5.jsは各種数学関数、図形描画関数、座標変換関数、shader関連などの便利なものが揃っているので、 シンプルにコードを書くことができそうです。
デバッグは直接index.htmlを開いてブラウザのdeveloper toolで行うようにしました。
描画jsファイル(sketch.js)
p5.jsのプロセスの特徴として、setup関数でページ読み込み時に初期化を行い、フレームカウントごとにdraw関数を呼び出して再描画されるようです。 setup関数はキャンバスサイズを設定してキャンバス要素を作成したり、背景などベースの描画を行います。 その後draw関数が1秒間にfps回呼び出されますが、そこでアニメーションなどといった動的な処理を記述します。 また今回は使用しませんが、最初に読み込みを行う前にpreload関数を実行することができ事前に画像ファイルなどを読み込むことができます。
グラデーションgif動画
一つ目の例としてグラデーションgif動画を作成します。 画像全体にhexgridを作成し、左上から右下にかけて虹色状に色に勾配をつけてグラデーションにします。 またフレーム毎にそれぞれの正六角形を回転させてさらにグラデーションも移動させます。
正六角形を回転させる場合、隣接同士のグリッドが重ならないように回転角度に応じて六角形の辺の長さを調節しなければなりません。 こういう場合はそれぞれの六角形に対して重心と外接円の半径と回転角を保持すれば楽です。 六角形の描画はdrawhex関数で実装していますが、多角形の描画はbeginShapeとendShape関数の間に頂点となる座標をvertex関数で設定すればいいです。
p5.createloop
またアニメーションをgif形式として保存するために、p5.createloopライブラリを使用します。 frameRate関数でループのfpsを設定、durationでループの継続時間を設定することができます。 またここでは使用していませんが、animLoop.thetaやanimLoop.progressでループの進行度を変数として取得することもできます。 その他noiseの付与などの機能があります。詳しくはドキュメント https://www.npmjs.com/package/p5.createloop で見られます。
const outgif = true; // const W = 180; // const H = 180; const W = 500; const H = 520; let rot = 1; function setup() { createCanvas(W, H) fill(0) frameRate(30) createLoop({duration:4, gif:outgif}); } function drawhex(x, y, r, angle) { let rad = PI/3; let r2 = r * sqrt(3) * 0.5 / cos(PI/6 - angle%(PI/3)); fill(angle*180 % 360, 50, 100); beginShape(); for(let i = 0; i < 6; ++i){ let px = cos(rad*i + angle) * r2 + x; let py = sin(rad*i + angle) * r2 + y; vertex(px, py); } endShape(CLOSE); } function draw() { background(0,0,0); colorMode(HSB); let radius = 10; const dy = radius * sin(PI/3); const dx = radius * 3; let r = 0; let c = 0; while(r * dy < H) { let y = r * dy; let offset_x = r % 2 == 0 ? 0 : radius*3/2; while(c * dx + offset_x < W) { let x = c * dx + offset_x; drawhex(x, y, radius, (r+c+rot)*TWO_PI/360); c += 1; } r += 1; c = 0; } rot += 1; }
フラクタル(Pascal Triangle)
またフラクタルといった数学パズルやアルゴリズムのようなものを利用して、描画を行うことができます。 ここでは例としてPascal Triangle(パスカルの三角形)を作成します。 パスカルの三角形は初期値として一番上の行の中央の数値を1、その他を0とし、 次の行は一つ上の行の二つの隣接マスの和から数値を設定するシンプルな動的計画法で構築できます。 それぞれの画素に設定する色はパスカルの三角形内のそれぞれの数値のmodulo 7で決定しています。
点の描画はstrokeで色を設定し、strokeWeightで点の大きさをピクセル単位で設定した後、point関数で座標を指定すれば可能です。 アニメーションとして出力しないのでsetup関数内ですべての描画を完了し、draw関数は空となります。 draw関数内に描画を行ってもいいですが、その場合フレーム毎に描画するのでCPUが働きっぱなしになります。
const outgif = false; // const W = 180; // const H = 180; const W = 900; const H = 900; const colors = ["black", "red", "orange", "yellow", "green", "blue", "purple"]; function setup() { createCanvas(W, H) fill(0) draw_pascal(); frameRate(30) createLoop({duration:4, gif:outgif}); } function draw_pascal() { background(0,0,0); let a1 = Array(W); a1.fill(0); a1[W/2] = 1; stroke(colors[1]); strokeWeight(2); point(W/2, 0); for(let r = 1;r < H;r += 2) { let a2 = Array(W); a2.fill(0); for(let c = 1;c < W-1;++c) { a2[c] = (a1[c-1] + a1[c+1])%7; stroke(colors[a2[c]]); strokeWeight(2); point(c, r); } a1 = a2; } } function draw() {}
パスカルの三角形は数値に対して規則的に色を塗ればシェルピンスキーのギャスケット形状になります。
まとめ
この記事に載せたgifや画像は小さいサイズですが、大きいものはinstagram(username: tk87_s_)に掲載します。 Generative Art関連の作品はinstagramで時々アップする予定です。
p5.jsは2Dだけでなく3Dオブジェクトも描画可能です。3Dに関してはthree.jsライブラリも面白そうです。