質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.13%

Unity2D Graphics.DrawMesh メッシュの結合

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 725

t2t21212

score 62

前提・実現したいこと

Graphics.DrawMeshを使って生成しているメッシュを結合して、FPSを改善させたい。

該当のソースコード

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

public class ExampleWorld : MonoBehaviour
{
    public int width;
    public int height;

    public ExampleTile[,] tiles;

    public Material material;

    Mesh mesh;

    // Use this for initialization
    void Start()
    {
        CreateTiles();
        GenerateMesh();
    }

    // Update is called once per frame
    void Update()
    {
        Graphics.DrawMesh(mesh, Vector3.zero, Quaternion.identity, material, 0);
    }

    void CreateTiles()
    {
        tiles = new ExampleTile[width, height];

        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                tiles[i, j] = new ExampleTile(ExampleTile.Type.Stone);
            }
        }
    }

    void GenerateMesh()
    {
        ExampleMeshData data = new ExampleMeshData(tiles);
        mesh = new Mesh();
        mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
        mesh.vertices = data.vertices.ToArray();
        mesh.triangles = data.triangles.ToArray();
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExampleMeshData
{
    public List<Vector3> vertices;
    public List<int> triangles;

    public ExampleMeshData(ExampleTile[,] data)
    {
        vertices = new List<Vector3>();
        triangles = new List<int>();

        for (int i = 0; i < data.GetLength(0); i++)
        {
            for (int j = 0; j < data.GetLength(1); j++)
            {
                CreateSquare(data[i, j], i, j);
            }
        }
    }

    void CreateSquare(ExampleTile tile, int x, int y)
    {
        vertices.Add(new Vector3(x + 0, y + 0));
        vertices.Add(new Vector3(x + 1, y + 0));
        vertices.Add(new Vector3(x + 0, y + 1));
        vertices.Add(new Vector3(x + 1, y + 1));

        triangles.Add(vertices.Count - 1);
        triangles.Add(vertices.Count - 3);
        triangles.Add(vertices.Count - 4);

        triangles.Add(vertices.Count - 2);
        triangles.Add(vertices.Count - 1);
        triangles.Add(vertices.Count - 4);
    }

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

public class ExampleTile { 
    public enum Type { Stone }
    public Type type;

    public ExampleTile(Type type)
    {
        this.type = type;
    }
}

試したこと

Graphics.DrawMeshを使って動的に生成しているメッシュを結合して一つにする方法を調べたのですが、出て来なかったので質問しました。
プロジェクトで使っているコードはもっと大量にメッシュを生成しているため、SetPass Callは2なのですが、saved by batchingが約30000以上あります。そのため、30~40FPS程度しか出ないので、動かすとカクカクしている状態です。
300×300のマップをそのコードで生成しようとすると、出力するのに10分以上かかってしまっています。
どのようにすればFPSを上げることが出来るのでしょうか? メッシュを結合すればいいのではないか、と自分は考えました。載せているコードは、最小限のものです。

回答お願いします。

実際のコードで生成した場合↓

イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

このソースだけ見ていると、ExampleWorldのMeshに頂点情報を集めて1MeshDrawを実行しているように見えるのですが、30~40FPSで動作しているものはこのソースを利用せずそれぞれのタイルで描画しているということですか?
他の質問を見るとDrawMeshInstancedなども確認されているようですが、ネックになっているのはCPU/GPUどちらかの切り分けはできているのでしょうか?
300x300は常に描画が必要なのでしょうか。300x300の一部だけだったらその範囲内だけ描画する(カリングする)とか。
ミニマップ状態で300x300が必要なら、一度それを構築した後RenderTextureからTextureに保持して描画するとかもあると思います。

ちなみに例えばですが、mesh.verticesをどこかのUpdateで

void Update()
{
  for (int i = 0; mesh.vertices.Length > i; ++i)
  {
    // なにか
  }
}


みたいにしているとしたら、mesh.verticesは中で逐次ToArrayをしているので一時変数で受け取ってからのほうが断然いいのですが、そんなことをしていたりしますか?

300×300のマップをそのコードで生成しようとすると、出力するのに10分以上かかってしまっています。

全く試してないですが、ExampleTileのclassを一つずつnewしているからな気もします。
ExampleTileの情報がもっと他にもあるなら別かもしれませんが、そうでないのならTypeだけの二次元配列にしてしまうとか、もうちょっと拡張データがあるならclassでなくstructにして一気に確保にするのは効果があると思います。
ただ、コンストラクタが引数付きで作られているので、コンストラクタ自体をなくす必要はあるかもしれません。

また、ExampleTileをstructにした場合、ExampleMeshDataのコンストラクタの引数が値渡しになったかは忘れました。
配列だから多分参照渡しなので問題ないですが、そうでなかったらrefキーワードで参照渡しにするとかしないとコピーが作られるため、コピー生成分処理を食います。

List.Addも確か容量不足のときに倍々で拡張してた気がするので、先に容量確保してからだと倍々拡張しないからそこそこ高速化できると思います。
変更はListのnewの部分だけで、Addの部分は変更しなくてもいいはずです。

// 300x300の4頂点分の容量を先に確保
vertices = new List<Vector3>(300 * 300 * 4);


ちなみにグリッドとして考えるとすべてのタイルにつき4頂点分確保はなくてもできるような気もします。(別の理由があるならこれしかないですが)
インデックスは今の状態が必要です。

GetLength()って内部のテンポラリサイズを参照していましたっけ...
毎回計算の可能性もあるので、for文の直前に一度保持してからその値を使うのも場合によっては効果があるかもしれません。(ないかもしれません)

生成部分についてはこんな感じで軽減できそうな気もします。
もちろん、ほかの何かの要因があれば話は別です。

追記
例えば今回の300x300が2D-RPGのワールドマップで、一画面に描画されるものが10x10で済む場合、
300x300-10x10 = 89900タイルの描画処理(DrawMesh)が無駄です。
上記の場合、自分だったら 10x10 の一回り分の 12x12 タイル分だけのMeshを作ります。
そして、描画範囲内の情報だけ(Materialや頂点の座標)を設定して描画自体の処理を限定させます。
キャラクタが移動したのだったら移動したときに範囲の情報を再設定します。
要は先に無駄と分かっている処理を省きます。

昔、MSのDirectX関連の偉い方(だったと思う)が言った言葉です。(若干ニュアンスは違うかもですが)

Q. 一番早いプログラムは?
A. 何もしないプログラム

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/07 15:09

    回答ありがとうございました。返信遅れて申し訳ないです。

    キャンセル

-3

質問いただきありがとうございます!
模様だけならテクスチャを使えば良いと思います。
t2t21212さんの生成したいメッシュがある程度複雑な形の場合は、Vertex ShaderでGPU側に処理させればテクスチャを形状に反映させることができます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る