0.このテキストの目的

本記事では、Scott Drave氏のFlameアルゴリズムについて、日本語でごく簡単な解説をしつつopenFrameworksとGLSLの実装例を示します。当該アルゴリズムは、動画などで広く流布しています。出力が非常に美しく多彩で、こんなにも耽美な絵画群がいかにして1つのプログラムで生成できるのか解読したいと思ったのがきっかけで、作者の論稿を読み始めました。
アルゴリズム自体は、その出力結果からは想像できないほど驚くほどシンプルでです。このアルゴリズムを発見し、OpenSourceで公開してくれたScott Drave氏に敬意をこめながら、コンピュータによる美しい画作りをしたいと思う方の少しでも参考になればと思い本記事を書きました。
注意:数学的な記述については専門的知識を持ち合わせておらず、間違い等などあればご指摘いただけると幸いです。

Flame algorithm, one of the famous iterated function systems, implemented with #openframeworks . #generativeart #creativecoding

Ayumu Nagamatsuさん(@ayumu_naga)が投稿した動画 –

Sketch of iterated function system. Like Pollock’s. #processing #generativeart

Ayumu Nagamatsuさん(@ayumu_naga)が投稿した写真 –

目次

1. Scott Drave氏の功績 – Electric Sheep と Flame
2. 反復関数系
3. Flame アルゴリズム
4. openFrameworks + GLSL での実装

1.Scott Drave氏の功績 – Electric Sheep と Flame

Electric SheepとはScott Dravesを中心として1999年に制作されたソフトウェアと、それを運用するアートプロジェクトです。当該ソフトウェアはフラクタル図形のアニメーションを描画するスクリーンセーバとし機能します。アニメーションは、高性能な中央サーバーがレンダリングし、クライアントコンピュータはインターネット経由でストリーミングするのみです。

生成される図像は、作者が Flame と呼ぶシンプルなアルゴリズムによるものです。これは1992年にOpenSourceとして公開( 現在もGitHubに存在 )、1993年にPrix Ars Electronica Honorary Mentionedを獲得しています。Flameはその美しさそ結果の多様さからを人気を博し、スタンドアロンのアプリケーションからAdobe After Effects や Adobe Photoshop のといったポピュラーなソフトで動作するプラグイン等が存在します。

Flameはその特質上、とりうるパラメータが無数にあります。Electric Sheepではできあがった絵(つまりFlameのとったパラメータ)を、ユーザーグループ間で共有し、気に入ったものに投票することができます。その結果を重み付けとした遺伝的アルゴリズムによって逐次あらたなパラメータを生みだし、絵を生成します。つまり、人間の好みを遺伝的アルゴリズムで学習させ、より美しい絵を永遠に生成していくということを意味します。この一連のプロセスは、人間と機械の協調によって美を追求する営みであり、人間が美しさを測る「審美」を再考する上でも興味深い試みです。

Scott Drave氏の功績として特筆すべきは
1) 美を目的とした、理解しやすいアルゴリズムを発見したこと
2) 発見したアルゴリズムを1992年という早いの段階でOpen Sourceとしたこと
3) 人とマシンの協調による美学の発展を志向したこと
です。アートが「美」を追求するものでなくなった今、近未来の美しさを考える美学の発展を考えるにあたって大きなマイルストンのように思います。
こうした美に着目した作家は木本圭子William Latham等挙げられますが、また別の機会にまとめたいと思います。

2.反復関数系

Flameは反復関数系(IFS: Iterated Function System)に属します。乱数を入力値として受け取り、再帰計算を経て座標変換します。再帰計算によって乱数の入力が、特定の系、集合による形をなすことがあります。例えば以下はその一例です。

シェルピンスキーのギャスケット

33.33..%:
33.33..%:
33.33..%:

シダ

1%:
7%:
7%:
85%:

Wikipediaよりパラメータ参照

上記の例はともに

という座標変換に帰着させることができます。
上記のa,b,c,d,e,fの6つのパラメータが、一点の確率で特定の値に決定され、再帰計算されることで、時として美しい形をなす事があるのです。逆に、秩序のないつまらない絵になるときもあります。不思議です。

3.Flame アルゴリズム

Flameアルゴリズムは http://flam3.com/flame.pdf に詳述されていますが、概観すると以下のようになります。

1)2次元ベクトルを入力し、座標変化をした上で2次元ベクトル出力する関数を、再帰的に計算する
2)この関数(座標変換処理)は、「アフィン変換」部と「バリエーション」部に別れ、それぞれのパラメータを確率にしたがって決定し実行する
・アフィン変換はパラメータを6つ、バリエーションは実質、無数のパラメータをとる
・このアフィン+バリエーションの関数のとるパラメータを複数用意し、それぞれに出現率をわりあてる。
・一回の再帰ループ(イテレーション)の中で、出現率に従って1つのアフィン+バリエーションの処理を実行する

アフィン変換部

上述した反復関数系(カオスゲーム)の通りです。これだけでも実に多様な系を生成することができます。

バリエーション部

それぞれの下位関数は、たとえば魚眼系の変換、天球系の変換など、上記の論稿では40種類以上定義されています。実質、オリジナルを定義することもできます。実装する際は、描画パフォーマンスと出力の好みによって決定します。

さらに、上記論稿では、点のカラーリング、パラメータ変更時のモーフィングについても言及していますが、このあたりの詳細は原文を参考ください。

4.openFrameworks + GLSL での実装

ここでは上記原理をGPUによって並列計算させることを念頭に、openFrameworksとGLSLでの記述をします。

main.cpp

下準備として、GLSL1.5を利用するために、OpenGL APIを3.2にします。
GLSL1.5を使う理由は、グローバル変数に in,out 修飾子が使え、openFrameworksが提供する変数(in vec4 positionなど)を使えるからです。(というかこれを推奨とofBookに記述がありました。)

ofApp.h / ofApp.cpp

openFrameworksのメインの記述は、指定の頂点数をもった Vertex Buffer Object (VBO) を用意し、そのオブジェクトに対して、Shader Material を適用します。オブジェクトにShader Materialを適用することで、頂点座標の計算、描画時の色計算をGLSLで記述することができます。計算内容やGPU性能によりますが頂点数100万くらいでも60fpsが十分でると思います。
描画のときに必要な変数を用意し、Uniform変数としてShaderにわたるようにします。
重要なのは、アフィン変換部、バリエーション部で使うパラメータです。

Vertex Shader

頂点計算は、Vertex Shaderで行います。Joel Castellanos氏によるWebGLでの実装例を参考にし、コードとして分かりやすくなるよう変数名など改変をくわえつつ、バリエーション部分は私の好きなようにカスタマイズしました。一般的にメモリ確保をする変数宣言は少なくすると良いと考えられますが、このコードは結構変数宣言をしてるのでチューニングの余地あると思います…。

Fragment Shader

Fragment Shader 側では、Vertex Shader側で計算した結果を出力するのみです。GLSLはバージョンが違うことで出力の際の記法や、使える機能にかなり差異があるので、ご注意ください。openFrameworksはデフォルトはGLSL1.2になります。(この場合、varyingの宣言ができたりします。gl_FragColorで色出力したりします。)

参考