私は美しい形態を生むアルゴリズムがとても好きです。本記事では、美しい形態を生む、アルゴリズムとしての「フラクタル」と「カオス」を簡単に紹介したいと思います。実行環境としては Processing を利用しました。是非、コピーして実行してみてください。
フラクタル
フラクタルとは、自己相似を繰り返し描いていくことで得られる図形を指します。図形または辺または頂点が指数的な増殖を引き起こし、複雑で美しい図形を描くことができます。フラクタルは1982年にブノワ・マンデルブロが「フラクタル幾何学」を著したことで、考えが広まったと言えます。それを受けて、1990年代には多くの研究者たちが、コンピュータ・グラフィクスの力を借りてカオス図形を発見し、BasicやC言語のプログラムで数々の図形がシミュレートされました。
今でも、ビジュアルプログラミングの学習において、フラクタルの説明が多くされます(書籍の Nature of Code や Generative Art にも!)。なぜ、フラクタル図形はそんなにも人々(とくにプログラマーや美術家)を魅了するのでしょうか。
私としては以下の3つのポイントが相互的にあるため、魅力を感じるように思います。
1.描画のルールは直感的に理解できる
後述のようにプログラム自体は、とてもシンプルにかけます。数式自体も長くはないので、理解が可能だと十分に言えます。
2.描画される結果は想像の域を上回る
一般にプラトン立体などの調和のとれた図形は想像しやすい図形といえますが、複雑でランダムな要素を持った図形を人間が直感的に想像するのは難しいです。にも関わらずシンプルな操作を指数的に反復することで、複雑性、荘厳さ、緻密さを兼ね備えた美しい絵ができあがるのです。
3.有機物を想起させる
一方で、人の想像を超えるけれども、どこか有機的な模様から、生物を彷彿とさせます。自然界においてフラクタルを発見することは非常に容易であり、形態学の本など、多くの場で論じられています。生物の発展プロセスとして、細胞分裂をする活動自体が、自己のコピーをおこなうことであり、フラクタルループといえます。
フラクタルを描画する
ここでは、定番の「シェルピンスキーのギャスケット」を書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
static final int MAX_LEVEL = 5; static final int VERT_NUM = 3; static final float k = 0.5; static final int alpha = 120; void setup() { size(800, 800); background(0); blendMode(ADD); noFill(); float x = width / 2; float y = height / 2; float r = 200; fractalLoop(r, x, y, 0); } void fractalLoop(float _r, float _x, float _y, int level) { if (level > MAX_LEVEL) return; stroke(12 + 30 * level, 30 * level, 12 + 50 * level, alpha); drawShape(_r, _x, _y); for (int i = 0; i < VERT_NUM; i++) { float x = _r * cos(TWO_PI / VERT_NUM * (i+0.5)) + _x; float y = _r * sin(TWO_PI / VERT_NUM * (i+0.5)) + _y; fractalLoop(_r * k, x, y, level + 1); } } void drawShape(float _r, float _x, float _y) { beginShape(); for (int i = 0; i < VERT_NUM; i++) { float x = _r * cos(TWO_PI / VERT_NUM * i) + _x; float y = _r * sin(TWO_PI / VERT_NUM * i) + _y; vertex(x, y); } endShape(CLOSE); } |
重要なのは、図像を決定するのに影響力を持つパラメータを取り出し、値を色々変えてみて結果を確かめることです。
上記のコードで、VERT_NUM = 3, k = 0.5 にすると見慣れた図形ができます。しかし、VERT_NUM = 11, k = 0.88 (頂点数をあげると計算量が指数的に増えるので、MAX_LEVEL = 3とかにする)にしてみてください。いきなり雰囲気が変わり荘厳な図形になります。
以下は、同じプログラムで、4つのパラメータのみに変化を加えて得られた図形になります。
カオス
カオスは、特定の多変数の漸化式でx, yを再帰的に計算し、結果を平面上にプロットしていくことで得られます。再帰計算という意味ではフラクタルと同じですが、決定論的に収束される系としての力学的な意味合いがカオスには含まれています。
漸化式で計算で得られる離散値は発散する場合もあれば、収束する場合もありますが、図形となるためには、収束する漸化式である必要があります。
例えば、漸化式に虚数の含ませると、不思議な収束をおこす場合があります。先人たちはこうした収束によって描ける図形(アトラクタともいう)を多く発見しました。ここではその例を紹介します。
方程式や収束の説明は省略しコードのみの記述とします。また以下のコードは、川上博「CによるカオスCG」の描画アルゴリズムを参考にして、Processingに移植したものになります。同著は、難解な数学的説明をC言語のプログラムで極力わかりやすすると同時に、図形の美しさに焦点をあてたとても良い本ですのでおすすめです。ただ絶版っぽいので図書館等でお探しください…
ローレンツ方程式
コメントアウトしたパラメータは、順にためしてみください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
float x = 0.1, y = 0.1; //static final float a = 0.45, b = 1.9; //static final float a = 1.0, b = 0.85; //static final float a = 1.0, b = 0.9; static final float a = 1.25, b = 0.75; void setup() { size(800, 800); blendMode(ADD); background(0); stroke(124, 155, 255, 50); } void draw() { float _x, _y; for (int i = 0; i < 100; i++) { _x = (1 + a * b) * x - b * x * y; _y = (1 - b) * y + b * x * x; point(_x * 100 + width*0.5, - _y * 100 + height*0.75); x = _x; y = _y; } } |
ローレンツ方程式による点描。幻想的な図形が描けます。#fractal day9 #generativeart #processing
Ayumu Nagamatsuさん(@ayumu_naga)が投稿した写真 –
チョーサ・ゴルビツキの方程式
コメントアウトしたパラメータは、順にためしてみください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
float x = 0.1, y = 0.1; //static final float a = -1.0, b = 0.05, c = 2.275, d = -0.5; //static final float a = 1.0, b = 0.0, c = - 2.25, d = 0.2; //static final float a = 1.0, b = 0.0, c = - 1.9, d = 0.4; //static final float a = -1.0, b = 0.1, c = 1.52, d = -0.8; //static final float a = -1.0, b = 0.1, c = 1.6, d = -0.8; //static final float a = 2., b = -0.2, c = - 1.75, d = 1.; static final float a = -2.0, b = 0., c = 2.6, d = -0.5; void setup() { size(800, 800); blendMode(ADD); background(0); stroke(124, 155, 255, 50); } void draw() { float _x, _y; float A; for (int i = 0; i < 100; i++) { A = a * (x * x + y * y) + b * x * (x * x - 3 * y * y) + c; _x = A * x + d * (x * x - y * y); _y = A * y - 2 * d * x * y; point(_x * 200 + width/2, - _y * 200 + height/2); x = _x; y = _y; } } |
以上、楽しんで頂ければ幸いです。