Processingで連続的な線のジェネラティブアートを制作

概要

クリエイティブコーディング用開発環境のProcessingを使ってこの投稿のタイトル画像の様な画像をプログラムで作りましょう!
プログラミングへの多少の理解は必要ですがなるべく簡単にしてあります。
修飾子とか結構適当なままなのは愛嬌だよっ!てへぺろ

今回作るモノ

こんなのとか


Processingとは?

Processingはアートプログラミングの為の開発環境で図形、画像、音、など色んな要素を掛け合わせて好き勝手面白いもの綺麗なもの作ろうぜ的なsomethingです。
クリエイティブコーディング向け開発環境は今回使用するProcessingだけでは無く、openFrameworks、TouchDesignerなどなどありますが、個人的にはProcessingが入門において扱い易いと思うのでこれ使います。
まずはダウンロード、インストールして新規プロジェクトを作りましょう。

構造の論理的な説明

この画像がどう作られているかの説明をします。
まず線1本1本がLineクラスのインスタンスです。Lineクラスは自作します。
Lineクラスは、

線の傾き(角度)
線の中点
線の端点(2つ)
線の長さ
を変数として持ちます。

大まかな流れ
メインのスクリプトでLineクラスの配列を宣言し、for文ぶん回して
各線の角度を少しずつズラしながら初期化します。
各線の中点の座標も少しずつズラしながら初期化します。
(線の長さはあらかじめクラスの定義に書いておきます。)

中点と角度が定まったので、三角関数で線の両端の点の位置を計算し各線に設定します。
両端の点が定まったら、2点を線で結ぶメソッドを使って全ての線を描画します。
以上!

ポイント
各線の角度、中点の座標を少しずつズラすと書きましたがここにポイントがあります。
パーリンノイズと言う乱数?を使用します。
よくある乱数はrandom()ですが、パーリンノイズはnoise()で扱うことが出来ます。
Processingにおいてrandom()もnoise()も基本的には0.0~1.0のfloat値を返しますが、random()は何度実行しても毎回ランダムに値を返します。
一方、noise()は引数で渡すfloat値を少しだけ大きく(もしくは小さく)すると返ってくるfloat値も少しだけズレた数になります。
これによって連続的な変化を簡単に作りだせます。
このQiitaの記事の方がわかり易いかも

実際のコードと解説

Line.pde
Lineクラスの解説をします。
Processingでは一般的に以下の画像の様にタブの右にある逆三角形から新規スクリプトを作成し、そこにクラスを書きます。

まず、Line.pdeのコード全体はこちら。

class Line{
    public float angle; //線の角度
    public PVector center, point1, point2; //線の中点、端点1、端点2
    public float length = 1200; //線の長さ(今回は全ての線が固定で1200px)

    //コンストラクタ
    Line(){
        center = new PVector();
        point1 = new PVector();
        point2 = new PVector();
    }

    //線の角度、中点の位置から両端の点を算出するメソッド
    public void CalPoint(){
        point1.y =  sin(angle)* length + center.y;
        point1.x =  cos(angle)* length + center.x;
        point2.y = -sin(angle)* length + center.y;
        point2.x = -cos(angle)* length + center.x;
    }
}

ちなみに、座標系は画面のピクセルと一致していて(0,0)が画面左上右に1ピクセル、下に1ピクセル移動したら(1,1)です。

Noise.pde

これがメインのスクリプトです。
まず、全体のスクリプトがこちら。

NoiseLine.pde
int lineNum = 600; //線の数
Line[] lines  = new Line[lineNum]; //線の配列
float xseed = random(1);
float yseed = random(1);
float angleSeed = random(1);
float angleSeedIncrease = 0.004;
float posSeedIncrease = 0.2;

void setup() {
    //fullScreen(P2D);
    size(1920, 1080, P2D); //画面のサイズを指定
    stroke(30, 180, 255, 100); //線の色を指定
    strokeWeight(0.5); //線の太さを指定
    for (int i = 0; i < lineNum; i++) {
        lines[i]  = new Line(); 
    }
    //このfor文で線の角度、中点の位置、両端の点の位置を決める
    for (int i = 0; i < lineNum; i++) {
        //角度を決める
        if (i <= 0) {
            lines[i].angle = noise(angleSeed) * 4 * PI;
        } else {
            lines[i].angle = noise(angleSeed) * 4 * PI;
        }
       
        if (i <= 0) {
            lines[i].center.x = noise(xseed) ;
            lines[i].center.y = noise(yseed) ;
        } else {
            lines[i].center.x =  lines[i-1].center.x + noise(xseed)*5 ;
            lines[i].center.y =  lines[i-1].center.y + noise(yseed)*3 ;
        }
        lines[i].CalPoint();
        angleSeed += angleSeedIncrease;
        xseed += posSeedIncrease;
        yseed += posSeedIncrease;
    }
}

void draw() {
    background(255);
    for (int i = 0; i < lineNum; i++) {
       float y1;
      float x1;
      float y2;
      float x2;
        y1 = lines[i].point1.y;
        x1 = lines[i].point1.x;
        y2 = lines[i].point2.y;
        x2 = lines[i].point2.x;
        line(x1, y1, x2, y2);
    }
}

細かく説明します。

int lineNum = 600;

ここで線の数を定義しています。

Line[] lines = new Line[lineNum];

ここでで線の配列を定義しています。

float xseed = random(1);
float yseed = random(1);
float angleSeed = random(1);
float angleSeedIncrease = 0.004;
float posSeedIncrease = 0.2;

ここでは、noise()に渡す引数を定義しています。
それぞれの線の中点のx座標、y座標、線の角度を少しずつズラすのでxseed、yseed、angleSeedを定義しています。
angleSeedIncreaseは、for文で角度を決める際に毎周angleSeedに足すことでズレを作ります。この値を大きくすればズレは大きくなり、値を小さくすればズレは小さくなります。
posSeedIncreaseも同様でxseedとyseedに足すことで中点の位置をズラします。

Processingでは実行時setup()が1度呼ばれます。
それ以後、draw()が毎フレーム実行されます。

size()は実行時に実行結果が映るウィンドウのサイズを決めるメソッドです。第三引数のP2Dを入れると画質が少し悪くなり処理が早くなります。今回の場合特にP2Dはあってもなくても大差無いとは思います。
コメントアウトされているfullScreen(P2D);はフルスクリーンのウィンドウで実行するメソッドです。こっちを使うときはsize()をコメントアウトしましょう。

stroke(30, 180, 255, 100);

stroke()は線の色を決めるメソッドです。引数は(R,G,B,アルファ値)です。(アルファ値は透明度のことです。)
色を変えたければここを変えましょう。値は0~255で濃さを決められます。

strokeWeight(0.5);

strokeWeight()は線の太さを決めるメソッドです。太さを変えたい場合はここをいじりましょう。

 for (int i = 0; i < lineNum; i++) {
   lines[i]  = new Line();
 }

このfor文は線の配列lines[]の要素にインスタンスを代入してます。Javaってこれやらないとダメなんですね。

for (int i = 0; i < lineNum; i++) {
    lines[i].angle = noise(angleSeed) * 4 * PI;
    if (i <= 0) {
         lines[i].center.x = noise(xseed) ;
         lines[i].center.y = noise(yseed) ;
    } else {
         lines[i].center.x =  lines[i-1].center.x + noise(xseed)*5 ;
         lines[i].center.y =  lines[i-1].center.y + noise(yseed)*3 ;
    }
    lines[i].CalPoint();
    angleSeed += angleSeedIncrease;
    xseed += posSeedIncrease;
    yseed += posSeedIncrease;
}

線の配列をfor文で回してます。
lines[i].angle = noise(angleSeed) * 4 * PI;で角度を決めています。
noise()に4πをかけているので、0.0~4πの間で値が返ってきます。
角度の値は「度」ではなくて「ラジアン」なので注意!

if (i <= 0) {
   lines[i].center.x = noise(xseed) ;
   lines[i].center.y = noise(yseed) ;
} else {
   lines[i].center.x =  lines[i-1].center.x + noise(xseed)*5 ;
   lines[i].center.y =  lines[i-1].center.y + noise(yseed)*3 ;
}

このif文で中点を決めています。
1つ前の線の中点を基準に少しズラしていきます。
xは0.0~5までの間で値が返ってきます。
yは0.0~3までの間で値が返ってきます。
画面のアスペクト比を考慮して適当にxの方を大きくしました。

lines[i].CalPoint();

角度と中点が定まったので、lines[i].CalPoint();で両端の点を計算します。

angleSeed += angleSeedIncrease;
xseed += posSeedIncrease;
yseed += posSeedIncrease;

各Seedの値をズラします。

void draw() {
   background(255);
   for (int i = 0; i < lineNum; i++) {
        float y1;
        float x1;
        float y2;
        float x2;
        y1 = lines[i].point1.y;
        x1 = lines[i].point1.x;
        y2 = lines[i].point2.y;
        x2 = lines[i].point2.x;
        line(x1, y1, x2, y2);
    }
}

draw()メソッドです。
background()は背景を塗るメソッドです。background(255)の場合は背景は白です。(R,G,B)でも出来ます。
line()メソッドは2点を線で結ぶメソッドです。(点aのx座標, 点aのy座標, 点bのx座標, 点bのy座標)で2点を引数として渡すと、点aから点bまで線が描画されます。
今回の記述だとフレームごとに変化がないのでdraw()に書くことはないですが、ここから何か動きをつけようとしたら必要になるので一応記述しておきました。自由に遊んであげてください。線達も喜びます。

終わり

以上で終わりです。
興味を持った人は、「ジェネラティブアート」とか「ジェネラティブデザイン」とかで検索してみると色々出てくると思いますし、本も売ってるので買ってみるといいと思います。

この記事が気に入ったら、サポートをしてみませんか?気軽にクリエイターを支援できます。

note.user.nickname || note.user.urlname

頂いたお金は、クリエイティブコーディングの本を買ったり勉強するのに使います。

え、うそ、ありがとう.....愛していいですか?
17

梶田悠

ジェネラティブアートのレシピ帳

ジェネラティブアート系のソースコードを解説していくマガジンです。各投稿のサムネイルにあるものを作れるようになります。 何人かで回せたらいいなと思ってます、お声掛け下さい。
1つのマガジンに含まれています
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。