Unityでプロシージャルモデリング (球)

Unityでメッシュをスクリプトでプロシージャルに生成する方法について何回かに分けて書いていきます。

* [第1回 Unityでプロシージャルモデリング \(三角形・四角形・正多角形\) \- Qiita](https://qiita.com/aa_debdeb/items/d34bd733e0584494bdbd)
* [第2回 Unityでプロシージャルモデリング \(平面\) \- Qiita](https://qiita.com/aa_debdeb/items/043695f998606876cfb4)
* [第3回 Unityでプロシージャルモデリング \(直方体\) \- Qiita](https://qiita.com/aa_debdeb/items/983c264879084bd40b0c)
* [第4回 Unityでプロシージャルモデリング \(円柱\) \- Qiita](https://qiita.com/aa_debdeb/items/c54b14696363873c640d)
* 第5回 Unityでプロシージャルモデリング (球)
* [第6回 Unityでプロシージャルモデリング \(トーラス\) \- Qiita](https://qiita.com/aa_debdeb/items/efc188aa3c03a8acc5b9)

今回は球を作ってみます。

![sphere_01.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/25102ba3-9d90-14fb-762c-7815ffed2eb7.png)

```csharp:Sphere.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Sphere : MonoBehaviour
{
    [SerializeField] private int thetaSegments = 5;
    [SerializeField] private int phiSegments = 8;

    private void Awake()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;

        Vector3[] vertices = new Vector3[2 + (thetaSegments - 1) * phiSegments];
        int[] triangles = new int[(phiSegments) * 2 * 3 + (thetaSegments - 2) * phiSegments * 2 * 3];

        float thetaStep = Mathf.PI / thetaSegments;
        float phiStep = Mathf.PI * 2.0f / phiSegments;
        int vi = 0;
        int ti = 0;

        vertices[vi++] = new Vector3(0, -1, 0);
        for (int hi = 1;  hi < thetaSegments; hi++)
        {
            float theta = Mathf.PI - hi * thetaStep;
            for (int pi = 0; pi < phiSegments; pi++)
            {
                float phi = pi * phiStep;
                vertices[vi++] = new Vector3(Mathf.Sin(theta) * Mathf.Cos(phi), Mathf.Cos(theta), Mathf.Sin(theta) * Mathf.Sin(phi));
            }
        }
        vertices[vi++] = new Vector3(0, 1, 0);

        for (int pi = 0; pi < phiSegments; pi++)
        {
            triangles[ti++] = 0;
            triangles[ti++] = pi + 1;
            triangles[ti++] = pi != phiSegments - 1 ? (pi + 1) + 1 : 1;
        }
        for (int hi = 0; hi < thetaSegments - 2; hi++)
        {
            for (int pi = 0; pi < phiSegments; pi++)
            {
                int pj = pi != phiSegments - 1 ? pi + 1 : 0;
                int v00 = pi + hi * phiSegments + 1;
                int v10 = pj + hi * phiSegments + 1;
                int v01 = pi + (hi + 1) * phiSegments + 1;
                int v11 = pj + (hi + 1) * phiSegments + 1;
                ti = MakeQuad(triangles, ti, v00, v10, v01, v11);
            }
        }
        for (int pi = 0; pi < phiSegments; pi++)
        {
            triangles[ti++] = vertices.Length - 1;
            triangles[ti++] = (pi != phiSegments - 1 ? pi + 1 : 0) + (thetaSegments - 2) * phiSegments + 1;
            triangles[ti++] = pi + (thetaSegments - 2) * phiSegments + 1;
        }

        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
    }

    private int MakeQuad(int[] triangles, int ti, int v00, int v10, int v01, int v11)
    {
        triangles[ti] = v00;
        triangles[ti + 1] = triangles[ti + 5] = v01;
        triangles[ti + 2] = triangles[ti + 4] = v10;
        triangles[ti + 3] = v11;
        return ti + 6;
    }
}
```

今回作成する球では、[球面座標系](https://ja.wikipedia.org/wiki/%E7%90%83%E9%9D%A2%E5%BA%A7%E6%A8%99%E7%B3%BB)での2つの角度`θ`と`φ`をそれぞれ`thetaSegments`と`phiSegments`の値で分割するように頂点を配置しています。

球は以下のような三角形で構成されています(分割数はデフォルト値)。頂点の配置などは異なりますが、[以前説明した円柱](https://qiita.com/aa_debdeb/items/c54b14696363873c640d)と同じように底面、側面、上面の三角形を順々に定義していきます。

![sphere_02.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/7bf16275-b025-297c-4e1b-a33e7857262f.png)

以下は球を見上げたときの頂点と三角形の構成を表しています。球の底面部分は円柱の底面部分と同じように定義しています。

![sphere_ex_bottom.png](https://qiita-image-store.s3.amazonaws.com/0/61569/d9ed5fd8-6de0-42c9-6398-0a0ea09b59bb.png)

球の側面部分も円柱と同様に平面の水平方向の端を繋げたように構成しています。

![sphere_ex_side.png](https://qiita-image-store.s3.amazonaws.com/0/61569/e266ae80-f6ed-52a6-e06a-6013ca86f464.png)

以下は球を上から見下ろしたときの頂点と三角形の構成で、球の上面も円柱の上面と同じように定義しています。

![sphere_ex_top.png](https://qiita-image-store.s3.amazonaws.com/0/61569/0bddebcd-0e13-c59b-b28b-790be20209b0.png)