Mandelbrot
今回はフラクタルの中でも有名なマンデルブロ集合をやっていく.
これはSiv3Dのサンプル1にもあるため、こちらを参考にすれば簡単に組める. なんならコピーだけで行ける
なので、これを参考にしつつ実際に描画してみよう.
まずマンデルブロ集合は以下の漸化式で描かれる.
この漸化式が収束しないときを黒で描画し、収束するときを収束速度で塗り分けるのが一般的なはず.
黒で塗られ場部分がフラクタルの形になるわけである.面白いね.
ただ、このままだと複素数なため、可視化が難しい...
そこで実際には実数に落とし込んで計算を行う.
実数への持って行く方法は非常にシンプル.
まず以下のように複素数を定義する.
これを実際に代入してみると以下のようになる
この時、実部と虚部を取ると,
という風に綺麗にまとまる.
さて、式が分かったなら計算すればいいだけなんだけど、もう一つ問題がある.
そもそも収束するのはどういう時なのかということである.
これは
と言える.
証明は省略するが、ここ2に書いてあるのが分かりやすいと思う.
さて、ここまで分かれば実装をするのみ!
まず最初にx-y軸は式内のa,bに対応するとする.
そして最初のx,yは0から開始する.
なので、こうしている感じ.
auto Mandelbrot = [](double a, double b)
{
double x = 0.0, y = 0.0;
// ...
}
そしたら実部と虚部を計算して、発散判定.
発散する場合は収束するまでの速度を描くために`n`を返すようにする.
もし360回以内に発散しない場合は、強制的に収束するものとする.
```c++
for (int32 n = 0; n < 360; ++n)
{
// 実部 x(n+1) = x(n)^2 - y(n)^2 + a
const double t = (x * x - y * y + a);
// 虚部 y(n+1) = 2*x(n)*y(n) + b
const double u = (2.0 * x * y + b);
// 発散は |z_m| > 2 で判定可能
// 発散する場合はその収束の速度を表示する
if (4.0 < (t * t + u * u))
{
return n;
}
x = t;
y = u;
}
return 0;
};
最後に実際に描画する場所を書く.
各Pixelに対してマンデルブロを計算する.
この値が0なら収束してるので、そこを黒にしてしまう.
0以外なら発散してるので、HSVに色を当てはめて収束速度を描画する.
for (auto y : step(resolutuion.y))
{
const double yPos = yb + (d * y);
for (auto x : step(resolutuion.x))
{
const double xPos = xb + (d * x);
// 単純にPixel毎に判定するだけ
if (const int32 m = Mandelbrot(xPos, yPos))
{
image[y][x] = HSV{ (240 - m), 0.8, 1.0 };
}
else
{
image[y][x] = Palette::Black;
}
}
}
これで結果を見てみると以下のようになる.

うん、いい感じ.見たかった図形がちゃんと描画されている.
これを起点に他にも図形が描けるので、今度はそれをやってみる予定でいる.