見出し画像

ひと工夫の試み:ノイズを使った曲線描画

 先日、少し時間ができたので、ノイズを使った曲線描画を試してみた。なかなか良い感じだったし、#つぶやきProcessing 化もできたので、Twitter にて発表した。

忘れないうちに、この時に考えたよしなしごとついて記しておこうと思う。

文字数削減前のコード

 まず、このコードを読みやすくするために、圧縮前のプログラムを掲載する:

def setup():
    size(500,500)
    colorMode(HSB)
    noStroke()

def draw():
 clear()
 for i in range(32768):
     f=frameCount*.01
     x=noise(i*.001,f)
     y=noise(x,i*.001+f,f)
     z=noise(x,y,i*.001)
     fill((i+f*99)/128%255,z*400,255,255-z*300)
     circle(x*1100-300,y*1100-300,z*z*9)


曲線描画の構造

 このプログラムでは、曲線は小さな円の集合として描画されている。なので、変動量の大きいところでは点線になってしまうが、このあたりはあまり気にしないことにしている。計算機がもっと高速になれば、刻み幅を細かくすることにより、これらの隙間を埋めることができるだろう。もちろん、そんなことはせずに、前回描いた位置から直線を描画するようにすれば、現在の計算機でも途切れることのない曲線が(折れ線の近似により)描かれるであろう(今回の作品の場合、私は最終的に #つぶやきProcessing で発表したかったので、このプログラムでは折れ線近似の戦略は採用しなかった)。

 コードをみてみると分かると思うが、x,y,z という変数が登場している。このうち x,y にて描画される小さな円の中心を決定する。z については後述する。

この x,y を「いい感じ」に変化させたいのである。しかも、できるだけ単純なコードで。変数 x については、曲線上の位置というか、曲線の始点からの順序とでもいうべきかーを表している変数 i と、描画した画像の回数から生成された変数 f により、値を決めている。

一方、y については、x と同様に変数 i と f も使いつつ、直前に計算した変数 x の値も利用している。これを変数 x を使わずに書き下すと次のようになる:

y = noise( noise(i*.001,f), i*.001+f, f)

つまり、y の値を決めるために noise 関数の引数にも noise 関数の値を用いている。このような方法をドメインワープという。

ドメインワープについて

 関数の定義域のことをドメイン(domain)と呼ぶ。ちなみに関数の取りうる値の範囲をレンジ(range)と呼ぶ。ノイズ関数の引数の値は、あたりまえであるが、ノイズ関数が対応できる値を入力しなければならない。つまり、ノイズ関数の引数はドメイン上の値となる。

noise 関数に、noise 関数を適用させた値を用いることは、このドメイン上の値を歪ませて関数に適用しているとも言える。例えば、noize(x) ではなく、noise( noise(x) ) とする場合は、内側の noise 関数により x の値を歪ませていると解釈するわけである。歪ませたり、ねじったりすることを英語ではワープと呼ぶ。なので、ノイズ関数の引数にノイズ関数の値を使う手法をドメインワープ(domain warp)と呼ぶわけである。

かなり乱暴な説明であるが、ドメインワープの話はここで終わり。興味のある人は検索してみると詳しい資料が出てくると思う。

色についての工夫

 とまあ、このような工夫して、いい感じの曲線になるように努力しているのが今回のプログラムである。これはいわば曲線の形に注目した工夫である。この作品では色についても、少し工夫を行っているのでそれを説明したく思う。

先程、説明しなかった変数 z が色に関与している。作者としては、この変数 z は曲線の奥行きを表す値として取り扱っている。そのため、

fill((i+f*99)/128%255,z*400,255,255-z*300)

として、z の値が大きい程、透明度が増す(アルファ値が減る)ようにしている。z は視点からの距離を表しているものとし、遠くのものほど透明度を上げて描画している。

これは曲線を構成するそれぞれの点(円)の発光エネルギーが一定であるならば、遠くのものはその光が減衰するはずだし、また、被写界深度表現を伴う場合であれば、そのエネルギーは画像内により大きく拡散するはずであるーという考えによる。

 なので、円を描く circle 関数の半径も、z が大きくなれば半径も大きくなるように設定している:

circle(x*1100-300,y*1100-300,z*z*9)

 …とはいうものの、この作品の動画をみてみると、狙った効果はあまり出てはいないようにも見える。このあたりが面白いところなのだと思う(もちろん、ある程度の奥行き表現はできているようにも思えるが)。

スケーリングとシフトについて

 ちなみに、x および y に関する式、1100 倍して 300 を引いているのは、ノイズ関数の値域が 0 〜 1 であり、描画サイズ(出力サイズ)が 500 x 500 であることを考慮して決めている。

単に 500 倍すれば 0 〜 500 の範囲に収まり、良さそうに思える。実際、私も最初はそのようにコーディングした。しかし、実際に生成された映像をみてみると、当然であるが、曲線の全体が描画ウィンドウ内に収まってしまい、なんともこじんまりとした作品というか、迫力も面白味のない作品となってしまった。

そのため、曲線の座標が取りうる範囲を -300 〜 800 程度、つまり、描画ウィンドウの外側に 300 ピクセル程度あるような状態にするべく、このような線形変換を行っている。

 …とまあ、いつもの如く、ざざっと書き下してみた。過去の記事も読み返してみると、結構、誤字脱字が見つかる。今回も多分あるのだろう。これらについても、また時間があったら直していきたいと思う。

この記事が気に入ったらサポートをしてみませんか?