改造して遊ぼう!距離関数とノイズで作るジェネラティブアート

2021年8月7日土曜日

Processing 作例 静止画 中級者向け

t f B! P L

👉 Read this article in English.

改造して楽しく遊べる、Processing で書いたシンプルなサンプル・コードを紹介します。

以前ご紹介した「パーリンノイズにひと工夫加えて面白い絵づくりに活かすテクニック」を使ったジェネラティブアート作品になっています。

距離関数とノイズで作ったジェネラティブアートの作例

コードには工夫の余地を沢山残しているので、改造して楽しく遊んでいただけると思います。

Worley ノイズを使って描くジェネラティブアート

Worley ノイズ とは?

Worley ノイズをざっくり説明すると、キャンバス上にいくつか点を取り、ある座標からそれらの点との距離に応じて何らかの描画を行うアルゴリズムです。

シフマン先生が解説されている、こちらの動画がわかりやすいです。


Worley ノイズの作例とサンプル・コード

Worley ノイズを使って、このようなジェネラティブアートを描くことができます。

モノクロの細胞分裂のような絵

コードは GPL で公開します。GPL の条項に基づいて、ご自由にお使いください。


/**
 * Worley ノイズ:最短距離にある点との距離を元に描画
 *
 * Processing 3.5.3
 * @author @deconbatch
 * @version 0.1
 * created 0.1 2021.08.07
 */

void setup() {
  size(780, 480);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  smooth();
  noLoop();
}

void draw() {

  // 基準点の数
  int pointNum  = floor(random(5.0, 30.0));
    
  // ランダムな基準点を取得
  ArrayList<PVector> points = randomPoints(pointNum, width, height);

  // 描画
  background(0.0, 0.0, 0.0, 100.0);
  drawWorley(points);

}

/**
 * randomPoints : ランダムに取った複数の点を PVector の ArrayList で返す
 * @param  _num   : 点の数
 * @param  _w, _h : ランダムに取る x, y 座標の範囲
 * @return ArrayList<PVector> : ランダムに取った複数の点
 */
public ArrayList<PVector> randomPoints(float _num, float _w, float _h) {

  ArrayList<PVector> rnds = new ArrayList<PVector>();
  for (int i = 0; i < _num; i++) {
    rnds.add(new PVector(random(_w), random(_h)));
  }
  return rnds;

}

/**
 * drawWorley : Worley ノイズの描画を行う
 * @param ArrayList<PVector> _ps : 基準となる点
 */
public void drawWorley(ArrayList<PVector> _ps) {

  float range  = max(width, height);
  float nsStep = 0.005;
  float dsMult = 1.0;
  
  noStroke();
  for (int iX = 0; iX < width; iX++) {
    for (int iY = 0; iY < height; iY++) {
      // 最短距離にある基準点との距離を取得
      float minDist = range;
      for (int i = 0; i < _ps.size(); i++) {
        float distance = dist(iX, iY, _ps.get(i).x, _ps.get(i).y);
        if (minDist > distance) {
          minDist = distance;
        }
      }
      // 取得した距離により明度を計算
      float nVal = minDist * dsMult * nsStep;
      float pBri = (nVal * 100.0) % 90.0;
      // モノクロで描画
      fill(0.0, 0.0, pBri, 100.0);
      rect(iX, iY, 1.0, 1.0);
    }
  }
}

/*
  Copyright (C) 2021- deconbatch

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;
*/

これは最短距離にある点との距離に応じて明度を変化させたものです。
細胞分裂みたいな絵になっています。それぞれの細胞みたいな塊の中に「基準点」があります。

こちらはある座標から最短距離にある基準点との間に直線を引いたものです。

複数の点から放射状の線が伸びる絵

同様に円や正方形を描いたものなど、Worley ノイズだけでもいろいろ楽しめます。

縦横の直線で描いたジェネラティブアート
歪んだ網タイツのような絵
直線を複雑に組み合わせた絵


Worley ノイズとパーリンノイズのコラボレーション

ここに少し手を加えると、Worley ノイズとパーリン・ノイズの組み合わせで描くジェネラティブアートとなります。

Worley ノイズの距離関数をパーリンノイズのパラメータに

Worley ノイズでは「ある点との距離」を求める距離関数が出てきます。


float distance = dist(iX, iY, _ps.get(i).x, _ps.get(i).y);


この距離関数をパーリンノイズのパラメータに入れると、このような図が描かれます。


noise(distance, x, y);

先の Worley ノイズのコードの下記部分を変更することで、本ジェネラティブアートを生成するコードになります。

変更前


float nVal = minDist * dsMult * nsStep;
float pBri = (nVal * 100.0) % 90.0;


変更後


float nVal = noise(minDist * dsMult * nsStep, iX * nsStep, iY * nsStep);
float pBri = nVal * 100.0;


コード全体の概要解説

Worley ノイズのコードは、概略下記の構成となっています。

  1. randomPoints() で描画の基準となるランダム配置の点を取得
  2. drawWorley() で描画

この構成は、本ジェネラティブアート生成のコードでも全く同じです。

randomPoints()

ランダムに x, y 座標の値を決め、それを PVector にして ArrayList へ格納したものを返します。
ArrayList へ格納する PVector の数はパラメータ _num で決まります。

衝突判定などしていないので、同じ値の PVector が重複、場合によっては ArrayList 中の全ての PVector が同じ値ということも、可能性としてはあります。

drawWorley()

先の randomPoints() で取得した複数の点を基準点として Worley ノイズ的な描画を行います。

今回は以下のアルゴリズムで描画しました。

  1. ある座標から最短距離にある基準点を探し、その点との距離を取得します。
  2. その距離に応じて明度を計算し、その座標上に点を打ちます。
  3. これをキャンバス上の全ての座標に対して行います。

この明度を計算するところで「距離に応じて変化するパーリンノイズ」を用いて、今回のジェネラティブアートを生成しています。

変数 nsStep と dsMult

変数 nsStep と dsMult は描画の調整用です。


float nsStep = 0.005;
float dsMult = 10.0;


nsStep はパーリンノイズの変化率、dsMult は距離の変化率の調整に使っています。


float nsStep = 0.001;
float dsMult = 10.0;

ぼんやりした塗りの絵


float nsStep = 0.005;
float dsMult = 30.0;

しゃきっとした塗りの絵

改造のためのポイント解説

ジェネラティブアート生成のポイントとなるのはこの部分です。


float nVal = noise(minDist * dsMult * nsStep, iX * nsStep, iY * nsStep);

距離と x, y 座標の3次元ノイズとなっています。

ここを距離だけの1次元ノイズとしてみると、こうなります。


float nVal = noise(minDist * dsMult * nsStep);

複数の点から同一の円弧が発生している絵

どの基準点も、距離に応じて同じように明度が変化するようになりました。

では、「基準点」そのものをノイズパラメータに加えたらどうなるでしょう?


// 最短距離にある基準点との距離を取得
float minDist = range;
int   minIndx = 0;
for (int i = 0; i < _ps.size(); i++) {
  float distance = dist(iX, iY, _ps.get(i).x, _ps.get(i).y);
  if (minDist > distance) {
    minDist = distance;
    minIndx = i;
  }
}
// パーリンノイズのパラメータに取得した距離を使う
float nVal = noise(minDist * dsMult * nsStep, minIndx);

点によって異なる濃淡の円弧が発生している絵

基準点毎に明度の変化具合に差が現れました。そのおかげで境界線のようなものも見えるようになりました。

よかったら遊んでみてください

パーリンノイズにひと工夫加えたジェネラティブアート作品の一例として、Processing のサンプル・コードを書いてみました。

今回の作例では、Worley ノイズ部分で最短距離にある基準点だけを使いましたが、これを2番めに近い点を使うとか、一番遠い点を使うなど面白そうなネタが沢山残っています。

描画部分では明度だけでなく、色相、彩度の変化もやりがいのあるところですね。

このコードを元に、まだまだいろいろ遊べると思います。よかったら遊んでみてください。

QooQ