LoginSignup
3
0

More than 5 years have passed since last update.

【Unity】Terrainを使ってMandelbroを動かしてみた♬

Last updated at Posted at 2018-08-16

ちょっと遊んでいたら、MandelbroをUnityで動かしているひとがいた。そして、Terrainを動かしている人がいる。
これはOculusGoで動くMandelbroが見られるかなと思いやってみました。

今回は以下の三つの参考の通りです。
【参考】
【Unity】terrainを繰り返して、無限に続く地面を作る
UnityのTerrainでマンデルブロしてみた
Pythonでカオス・フラクタルを見よう!

今回のコードは以下に置いた
MuAuan/Unity

やったこと

(1)Mandelbro集合
(2)Unityで見る
(3)Terrainを動かす

(1)Mandelbro集合

pythonだと参考③から以下のとおり

def mandel(X, Y):
    a, b = [0] * 2
    for i in range(N):
        a, b = a**2 - b**2 + X, 2 * a * b + Y
    return a**2 + b**2 < 4
x, y = [np.linspace(-2, 2, M)] * 2
X, Y = np.meshgrid(x, y)
plt.pcolor(X, Y, mandel(X, Y))
plt.show()

Wikipediaによれば
次の漸化式

 {\begin{cases}z_{{n+1}}=z_{n}^{2}+c\\z_{0}=0\end{cases}}

で定義される複素数列 {zn}n∈N∪{0} が n → ∞ の極限で無限大に発散しないという条件を満たす複素数 c 全体が作る集合がマンデルブロ集合である。
マンデルブロ集合を複素数を使わずに書き直すには、zn を点 (xn, yn) に、c を点 (a, b) にそれぞれ置き代えて、

{\begin{cases}x_{{n+1}}=x_{n}^{2}-y_{n}^{2}+a\\y_{{n+1}}=2x_{n}y_{n}+b\end{cases}}

とすればよい。ということで、上記のPythonコードでよいことが分かる。上記コードはReturnが秀逸だ。以下蛇足。。
「上式で z0 を 0 以外の複素数にした場合、マンデルブロ集合の周が変形し、ジュリア集合に似たフラクタル状の曲線が現れる。」
確かに絵は変わるけど。。。オリジナルの方が綺麗。因みにコードは

N, M = 50, 500
a0=-0.5
b0=-0.7
def mandel(X, Y):
    a, b = a0,b0 
    for i in range(N):
        #a,b = a*a- b*b+ X,2 * a * b + Y
        b1=b
        b = 2 * a * b + Y
        a = a*a- b1*b1+ X
    return a*a + b*b < 4
x, y = [np.linspace(-2, 2, M)] * 2
X, Y = np.meshgrid(x, y)
plt.pcolor(X, Y, mandel(X, Y))
plt.pause(3)
plt.savefig('plot_mandela{}b{}.png'.format(a0,b0), dpi=60)
plt.close() 

(2)Unityで見る

これはまずは参考②のまんまです。コードも公開されているのでそのままで動くと思います。しかし、ウワンが独自にやってみた感じだと、Terrainの性質が少し異なってしまいました。
mandelebro.jpg
上の絵のように、二つのマンデルブロが見えますが、一つは公開されているassetsを使っています。何が異なるかというと、ウワンのは表示されているマンデルブロの高さを調整していますが、公開版は無調整でこの高さになっています。
一応、以下にウワンのコードを公開しておきます。
※元のコードは参考②を見てください

    IEnumerator UpdateTerrainCoroutine()
    {
        var heights = new float[terrainData.heightmapWidth, terrainData.heightmapHeight];
        while (true)
        {
            renderArea *= 0.98;
            var startX = baseX - renderArea / 2.0;
            var startY = baseY - renderArea / 2.0;
            var renderAreaPerWidth = renderArea / terrainData.heightmapWidth;
            var renderAreaPerHeight = renderArea / terrainData.heightmapHeight;
            for (int x = 0; x < terrainData.heightmapWidth; x++)
            {
                for (int y = 0; y < terrainData.heightmapHeight; y++)
                {
                    heights[x, y] = MandelbrotCalculator.Calculate(
                        startX + x * renderAreaPerWidth,
                        startY + y * renderAreaPerHeight);
                    heights[x, y] = heights[x, y] / 10;//ここ追加
                }
            }
            terrainData.SetHeights(0, 0, heights);
            yield return null;
        }
    }

ちなみにマンデルブロの部分は元コードのままで以下の通りです。

public class MandelbrotCalculator
{
    const int CALCULATE_LIMIT = 50;
    public static float Calculate(double baseX, double baseY)
    {
        double x = 0.0, y = 0.0, cacheX = 0.0, cacheY = 0.0;
        for (int i = 0; i < CALCULATE_LIMIT; i++)
        {
            cacheX = x * x - y * y + baseX;
            cacheY = x * y * 2.0 + baseY;
            if (cacheX * cacheX + cacheY * cacheY > 4.0)
            {
                return (float)i / CALCULATE_LIMIT;
            }
            x = cacheX;
            y = cacheY;
        }
        return 1f;
    }
}

このコードでは発散するというか4を超えたときの次数の最大回数との比を返しており、超えない場合1を返しているので、場所により高さが異なります。
ちなみにこれを見るのはその大きさが最初大きすぎて何を見ているのかわからずカメラの位置などに苦労しました。
Terainは初めて使いましたが、hierarchyの3Dの下にあり、hierarchyに張り付けました。あとは用意したスクリプトMandelbrotTerrainを貼り付けます。しかしここで、terraindataにMandelbroTerrainを指定するとTerrainColliderのところでエラーが出てしまいます。まあ、ここはNewTerrainのまま動きました。もう一方のMandelbrotCalculatorは前者から呼ばれているだけで特に何もしません。
以下、3連結した絵です。
mandelbroTerrain.jpg

(3)Terrainを動かす

こうしてほぼまんまでマンデルブロがUnityの中で動きます。
しかし、もっとダイナミックな絵を見たいということで、Terrainを動かすというのを追加してみました。
こちらは、以下のコードをUpdateに追加するだけ。。のはずでした。

public class MoveTerrain : MonoBehaviour {
    public float speed;
    float size = 2000;  //NOTE!
    // Update is called once per frame
    void Update () {
        transform.Translate (0, 0, speed);
        if (this.transform.position.z + size < 0) {
            Debug.Log("out of display");
            this.transform.Translate(0, 0, size * 2);
        }
    }
}

しかし、float size = 2000; //NOTE!がTerrainの大きさなんだけど、これウワンのは500でした。ということで、ここ変更しました。
それでもなんとなく動かしている方向が異なるらしく、結局以下のコードで連続する大地というか連続するマンデルブロができました。

    // Update is called once per frame
    void Update()
    {
        transform.Translate(0, 0, speed);
        if (this.transform.position.z >500)
        {
            Debug.Log("out of display");
            this.transform.Translate(0, 0, -1000 );
        }
    }

ということで、最初の500の符号と不等号が異なっています。

さて、OculusGoで見てみると。。。あれ??なぜか見えません。
ということで、動画は無しです。
残念。。。
man.gif
上記は二連結した場合のシーンです。三連結は迫力さらにアップしました。
【追記】認証で失敗していたようです。OculusGoで見えました!
【OculusGo】Mandelbroの大地が動く♪

※画像をクリックするとYouTube動画につながります

まとめ

・Terrainにマンデルブロを貼り付けて動かしてみた。
・さらにTerrainを動かして複数マンデルブロが続々出てくる無限ループを作成した

OculusGoではマンデルブロ(Terrain)が見えない
・同じように動くがパラメータ(TerrainColliderのTerrainData)が参考サイトのものと異なるが設定しようとするとTerrainにエラーが出る
・Gameの中に入って(その視線で)Terrainを散歩したい

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0