simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:頂点生成三角柱のインスタンスの統合による高速化

前回 15 x 15 x 15 の計3375個の三角柱を生成したとき、その時のインスタンス作成処理に 0.3秒ほど要していました。
ここから先、画面を埋め尽くすほどのブロックの数を表示しようというのに、もう既に諦めなければならないほど低速です。
この処理を高速化するため、インスタンスの数を減らして、メッシュを統合する方法をとってみます。

これも昔行った手法です。
simplestar-tech.hatenablog.com

メッシュ作成の結果、見た目は同じでも
f:id:simplestar_tech:20180922175632j:plain

インスタンス生成時の負荷は 11.84 ms (頂点バッファ確保と計算が主な仕事)
f:id:simplestar_tech:20180922175703j:plain

メッシュ作成時の負荷は 4.72 ms (一気に 5000 頂点分の設定のため多少時間を要する)
f:id:simplestar_tech:20180922175918j:plain

でした。
もともとの三角柱ごとにインスタンスを作る方法は、インスタンス生成時に 267.92 ms 要していました。
f:id:simplestar_tech:20180922180132j:plain

もともとの三角柱ごとにインスタンスを作る方法は、メッシュ作成時に 58.73 ms 要していました。
f:id:simplestar_tech:20180922180330j:plain

もともとの三角柱ごとにインスタンスを作る方法は、このチャンク(16 x 16 x 16 の三角柱の塊り)一つ作る場合でもフレームレートが維持できませんでしたが
メッシュを結合してインスタンスを統合することで、メッシュ作成処理の負荷が抑えられ、フレームレートをなんとか維持できる処理に変わりました。

以降の記事では、この 16 x 16 x 16 のチャンクをしばらく利用します。

具体的なコードは以下、よろしければ参考にしてください。(主に自分用)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class Prism : MonoBehaviour
{
    [SerializeField]
    private Material _mat;

    const float root3 = 1.732051f;

    private Mesh _mesh;
    private MeshFilter _filter;

    const int SIDE = 16;
    private Vector3[] _vertices;
    private Vector3[] _normals;
    private Vector2[] _uvs;
    private int[] _triangles;

    // Use this for initialization
    void Start()
    {
        _vertices = new Vector3[SIDE * SIDE * (SIDE / 2) * vertIndices.Length];
        _normals = new Vector3[_vertices.Length];
        _uvs = new Vector2[_vertices.Length];
        _triangles = new int[_vertices.Length];
        for (int x = 0; x < SIDE; x++)
        {
            for (int y = 0; y < SIDE; y++)
            {
                for (int z = 0; z < SIDE; z += 2)
                {
                    for (int i = 0; i < vertIndices.Length; i++)
                    {
                        int vertIndex = (x * SIDE * (SIDE / 2) + y * (SIDE / 2) + z / 2) * vertIndices.Length + i;
                        ref Vector3 vert = ref _vertices[vertIndex];
                        ref Vector3 normal = ref _normals[vertIndex];
                        ref Vector2 uv = ref _uvs[vertIndex];
                        vert = positions[vertIndices[i]];
                        vert.x += x * 2;
                        vert.y += y;
                        vert.z += z * root3;

                        normal = normals[i];
                        uv = uvs[i];
                        _triangles[vertIndex] = vertIndex;
                    }
                }
            }
        }
        _mesh = new Mesh();
        
        _filter = GetComponent<MeshFilter>();

        var renderer = GetComponent<MeshRenderer>();
        renderer.material = _mat;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            CreateMesh();
        }
    }
    private void CreateMesh()
    {
        _mesh.vertices = _vertices;
        _mesh.triangles = _triangles;
        _mesh.uv = _uvs;
        _mesh.normals = _normals;
        _filter.sharedMesh = _mesh;
    }

    static Vector3[] positions = new Vector3[] {
        // top
        new Vector3 (0f, 1f, 0f),
        new Vector3 (1f, 1f, -root3),
        new Vector3 (-1f, 1f, -root3),
        // bottom
        new Vector3 (0f, 0f, 0f),
        new Vector3 (-1f, 0f, -root3),
        new Vector3 (1f, 0f, -root3),
    };
    static int[] vertIndices = new int[]
    {
        0, 1, 2,
        3, 4, 5,
        0, 2, 4,
        4, 3, 0,
        2, 1, 5,
        5, 4, 2,
        1, 0, 3,
        3, 5, 1
    };
    static Vector3[] vertices = new Vector3[vertIndices.Length];
    static Vector2[] uvs = new Vector2[]
    {
        // top
        new Vector2 (.25f, 0.5f),
        new Vector2 (0.5f, 0.0f),
        new Vector2 (0.0f, 0.0f),

        // bottom
        new Vector2 (.75f, 0.0f),
        new Vector2 (0.5f, 0.5f),
        new Vector2 (1.0f, 0.5f),

        // side R
        new Vector2 (0.0f, 1.0f),
        new Vector2 (.33f, 1.0f),
        new Vector2 (.33f, 0.5f),

        new Vector2 (.33f, 0.5f),
        new Vector2 (0.0f, 0.5f),
        new Vector2 (0.0f, 1.0f),

        // side G
        new Vector2 (.33f, 1.0f),
        new Vector2 (.66f, 1.0f),
        new Vector2 (.66f, 0.5f),

        new Vector2 (.66f, 0.5f),
        new Vector2 (.33f, 0.5f),
        new Vector2 (.33f, 1.0f),
        
        // side B
        new Vector2 (.66f, 1.0f),
        new Vector2 (1.0f, 1.0f),
        new Vector2 (1.0f, 0.5f),

        new Vector2 (1.0f, 0.5f),
        new Vector2 (.66f, 0.5f),
        new Vector2 (.66f, 1.0f)
    };
    static Vector3[] normals = new Vector3[]
    {
        // top
        new Vector3 (0f, 1f, 0f),
        new Vector3 (0f, 1f, 0f),
        new Vector3 (0f, 1f, 0f),

        // bottom
        new Vector3 (0f,-1f, 0f),
        new Vector3 (0f,-1f, 0f),
        new Vector3 (0f,-1f, 0f),

        // side R
        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),

        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),
        new Vector3 (-0.8660254f, 0f, 0.5f),

        // side G
        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),

        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),
        new Vector3 (0f, 0f,-1f),
        
        // side B
        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),

        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),
        new Vector3 (0.8660254f, 0f, 0.5f),
    };
}