知ってよかった「p5.js」でのお絵描きテクニック3つ

With
知ってよかった「p5.js」でのお絵描きテクニック3つ はコメントを受け付けていません。

まだまだこの本の話を続けます。

■ [普及版]ジェネラティブ・アート―Processingによる実践ガイド


この本の各章で紹介されている「考え方」は、自分で作品を作っていこうとするときに、プログラミングとは別の部分での「基礎」となるものが多く、大変参考になりました。サンプルコードは、その考え方を言語で実装する場合の例になっています。私の場合、サンプルをp5.jsに移植しながら動かしていたのですが、その過程で、プログラムドローイングに必須の「基本テクニック」と呼べるようなものが、繰り返し出てきていることに気付きました。

個人的に、特に「知ってよかった」と感じたのは、

・sin、cosを使って座標を取る方法
・オブジェクト指向の基本
・noise関数の存在

の3つです。恐らく、プログラミングに慣れた人にとっては「何を今さら」な感じなのでしょうが、初学者が次のステップに進んでいくあたり、これらの要素を知っているか、使えるかというのは、その後の展開に大きな違いを生むのではないかと思います。

sin、cosで座標をとって図形を描く

たしか、高校の数Iで出てきた三角関数の正弦定理と余弦定理。いわゆる「サイン(sin)」と「コサイン(cos)」。「原点を中心とする半径rの円と、x軸とθ度で交わり原点を通る直線とが交差する座標を求めよ」…って、なんのためにこんなワケのわからんムダなことさせるんじゃ、○ね、と当時は思っていたのですが、私にとっては「30年くらい後にプログラムで絵を描くため」であったことが判明しました。ムダとか言ってすみませんでした。

ゆくゆく物理シミュレーション的なことなどをやろうと思い立ってしまうと、きっと文字どおり「○ぬ」ほど難しい公式を使わなくてはならなくなるのでしょうが、簡単な絵を描くのであれば、下の対応さえ覚えておけば、結構いろいろなことができるようになります。

y軸の正方向が「下」になるのは、コンピュータで絵を描くときの伝統的なお約束です。

で、単位時間あたりに、この「θ」が同じ量ずつ変化するとき、同時に原点の位置がx軸上を等間隔に動いていけば、円と直線の交わる座標の軌跡は、いわゆる「サイン(コサイン)カーブ」と呼ばれる曲線を描きます。

var centY;
var x = 0;
var y = 0;
var r = 50;
var theta = 0;

function setup() {
    createCanvas(600, 200);
    centY = height/2;
    background(230);
    strokeWeight(1);
    stroke(255);
    line(0, 100, 600, 100);
    drawCurve();
}

function drawCurve() {
    for (var i=0; i<width; i++) {
    strokeWeight(2);
    stroke(0);
    x++;
    y = centY+r*sin(radians(theta));
    theta+=2;
    point(x, y);
    }
}

原点の位置を動かさずに、θだけを増やしていくと、軌跡は原点を中心にrの半径を持つ円になります。で、実はこの方法を応用すると、円だけでなくさまざまな「正多角形」を、作り付けの関数に頼らずに描けるようになります。前のエントリでp5.jsとProcessingのコードの違いを説明するために作ったサンプルでは、その方法で3~8角形を描いています。

●[DEMO] 3~8角形までの図形をsinとcosで座標をとって描く(クリックすると別ウインドウが開きます)

function setup() {
    createCanvas(600, 600);
    background(255);
    drawcross();
    for (var h=3; h<9; h++) {
        drawpolygon(h);
    }
}

function drawcross() {
    stroke(200);
    strokeWeight(1);
    for (var j=200; j<=400; j+=200) {
        for(var i=150; i<=450; i+=150) {
            line(i, j-70, i, j+70);
            line(i-70, j, i+70, j);
        }
    }
}

function drawpolygon(peak) {
    var centX = 150 * ((peak%3)+1);
    var centY = 200 * floor(peak/3);
    var peakX = centX;
    var peakY = centY-55;
    var lastpeakX = peakX;
    var lastpeakY = peakY;
    var anglestep = 360/peak;
    for (var k=-90; k<=271 ; k+=anglestep) {
        peakX = centX+cos(radians(k))*55;
        peakY = centY+sin(radians(k))*55;
        stroke(0);
        strokeWeight(2);
        line(lastpeakX, lastpeakY, peakX, peakY);
        lastpeakX = peakX;
        lastpeakY = peakY;
    }
}

すべての図形は最後に定義している「drawpolygon」関数で描いているのですが、具体的にやっていることは、

・引数として図形の頂点数(3~8)をもらう
・スタート時のθは-90度(画面の一番上の頂点がくる場所)
・-90度から270度までを、360を頂点の数で割った分の角度ずつ回していき、その場所の座標をcosとsinでとる
・前の座標と新しくとった座標の間を直線でつなぐ
・一周したら関数での処理を終わる(一旦戻って次の図形へ)

という同じ処理になっています。この仕組みの応用と組み合わせで、いろんな絵が描けそうです。例えば、正5角形や6角形の頂点をすべて配列にとっておき、各点の間を線で結べば五芒星や六芒星も描けますね。また、原点を動かさず、半径(r)を段階的に増やしていくことで、軌跡は「渦巻き」状になります。

「ジェネラティブアート」では、第4章「円を描く間違った方法」で、このやり方を説明しており、応用として、半径と回転角の変化にランダム性を加えた「ノイズの多いらせん」の描き方を解説しています。

●[DEMO] “ノイズの多いらせん” powered by p5.js(クリックすると別ウインドウが開きます)

サンプルでは、リロードごとに100個のランダムならせんが画面上に描かれますが、らせんの数を減らしたり増やしたりすると、また違った感じのできあがりになります。

子どもが鉛筆やボールペンでグリグリと落書きをした感じですが、プログラムでこうした絵が作れるのって面白いですよね。

仕組みの「組み合わせ」を増やし「ランダム性」を洗練させていった結果が、以前紹介した「Wave Clock」のような「作品」になります。

●[DEMO] “Wave Clock” powered by p5.js(クリックすると別ウィンドウが開きます)

Wave Clockでは、見えない円の「半径」「原点」をなだらかに変化させつつ、その円周上を速度を変えて移動する点と、原点対称にある点とを、さらに変化する色の線で結んでいます。

シンプルな仕組みを多層に組み合わせ、そこに何らかの不確定要素(乱数とかノイズとか)を加えてやることで、毎回異なる、予想外の結果(絵)が出てくるというのが、ジェネラティブアートの面白さのひとつにあるのではないかと思います。

残る2つのテクニック「オブジェクト指向」「noise関数」については、そのうちまた別のエントリにしようと思います。