Densyakunのブログ

Qiitaもやってます: https://qiita.com/Densyakun

【Unity】フラクタル地形を生成する

 

gyazo.com

こんにちは電車君です。

今回はUnityでフラクタル地形を生成する方法を紹介します。

 ここで紹介するフラクタル地形とは、

「中点変位法」を用いた方法を使います。

フラクタル地形 - Wikipedia

 

まず、四角形を作成します。

 

Mesh mesh = new Mesh ();

mesh.vertices = new Vector3{ Vector3.zero, Vector3.forward, Vector3.right + Vector3.forward, Vector3.right };
mesh.uv = new Vector2
{ Vector2.zero, Vector2.up, Vector2.right + Vector2.up, Vector2.right };
mesh.triangles = new int{ 0, 1, 2, 2, 3, 0 };

mesh.RecalculateBounds ();
mesh.RecalculateNormals ();

 

この四角形は二つの三角形で出来ています。

頂点は4つあり、最初の頂点は四角形の左下の頂点です。

四角形の左下の座標は0,0(X,Y)です。

次に、左上、右上と、右下の頂点があります。

 

Vector3 verts = mesh.vertices;

for (int a = 0; a < verts.Length; a++) {
    verts [a].y = Random.Range (0f, height);
}

mesh.vertices = verts;

for (int a = 0; a < fineness; a++) {
    int b = mesh.vertices.Length;
    mesh = FractalTerrain.Subdivide_Half (mesh);

    verts = mesh.vertices;
    while (b < verts.Length) {
        float c = height / 2 / (a + 1);
        setVert (verts, verts [b], verts [b] + Vector3.up * Random.Range (-c, c));
        b++;
    }
    mesh.vertices = verts;
}

 

FractalTerrain.Subdivide_Half (mesh); は、

メッシュの細分化をします。

 

public static Mesh Subdivide_Half (Mesh mesh)
{
    Mesh m = mesh_copy (mesh);

    List<Vector3> newverts = new List<Vector3> (m.vertices);
    List<Vector2> newuv = new List<Vector2> (m.uv);
    List<int> newtris = new List<int> ();

    for (int c = 0; c <= m.triangles.Length - 3; c += 3) {
        int vn0 = m.triangles [c];
        int vn1 = m.triangles [c + 1];
        int vn2 = m.triangles [c + 2];

        Vector2 vu0 = m.uv [vn0];
        Vector2 vu1 = m.uv [vn1];
        Vector2 vu2 = m.uv [vn2];

        newverts.Add ( (m.vertices [vn0] + m.vertices [vn1]) / 2);
        newverts.Add ( (m.vertices [vn1] + m.vertices [vn2]) / 2);
        newverts.Add ( (m.vertices [vn2] + m.vertices [vn0]) / 2);

        newuv.Add ( (vu0 + vu1) / 2);
        newuv.Add ( (vu1 + vu2) / 2);
        newuv.Add ( (vu2 + vu0) / 2);

        int _vn = newverts.Count - 3;

        newtris.Add (vn0);
        newtris.Add (_vn);
        newtris.Add (_vn + 2);

        newtris.Add (vn1);
        newtris.Add (_vn + 1);
        newtris.Add (_vn);

        newtris.Add (vn2);
        newtris.Add (_vn + 2);
        newtris.Add (_vn + 1);

        newtris.Add (_vn);
        newtris.Add (_vn + 1);
        newtris.Add (_vn + 2);
    }

    m.vertices = newverts.ToArray ();
    m.uv = newuv.ToArray ();
    m.triangles = newtris.ToArray ();

    mesh.RecalculateBounds ();
    mesh.RecalculateNormals ();

    return m;
}

 

メッシュの細分化はそれぞれの三角形を細分化します。

三角形を細分化するには、辺の中点を作り、

3つの中点を結んだ辺を作るイメージで、

4つの三角形を作成します。

 

細分化したら新しい頂点を移動させます。

この時、新しい頂点は同じ場所に複数の頂点がある場所があります。

四角形を細分化するため、四角形の真ん中にある点が重複しています。

 

この重複している頂点を結合せず一度に移動させます。*1

 

public static void setVert(Vector3[] verts, Vector3 target, Vector3 result) {
    for (int a = 0; a < verts.Length; a++) {
        if (verts [a] == target) {
            verts [a] = result;
        }
    }
}

 

以下ソース

 

gist.github.com

 

*1:頂点を結合しようと思ったのですが、うまく行きませんでした