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

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

ただいまの
回答率

88.13%

Unity2D Graphics.DrawMesh

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 873
退会済みユーザー

退会済みユーザー

前提・実現したいこと

Graphics.DrawMeshを使って、正方形のメッシュを大量に描画したい。

該当のソースコード

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

public class ExampleClass : MonoBehaviour
{
    Mesh mesh;
    public Material material;

    private void Start()
    {
        mesh = new Mesh();
        mesh.vertices = new Vector3[] {
        new Vector3 (-1f, -1f, 0),
        new Vector3 (-1f,  1f, 0),
        new Vector3 (1f , -1f, 0),
        new Vector3 (1f ,  1f, 0),
    };

        mesh.uv = new Vector2[] {
        new Vector2 (0, 0),
        new Vector2 (0, 1),
        new Vector2 (1, 0),
        new Vector2 (1, 1),
    };

        mesh.triangles = new int[] {
        0, 1, 2,
        1, 3, 2,
    };

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

    void Update()
    {
        Graphics.DrawMesh(mesh, Vector3.zero, Quaternion.AngleAxis(0.0f, new Vector3(1.0f, 0.0f, 0.0f)), material, 0);
    }
}

試したこと

正方形のメッシュをGraphics.DrawMeshを使って描画しました。ですが、100×100のように、メッシュを大量に描画するやり方が分かりません。

試行錯誤しましたが、どうやっていいか、どうしても分かりませんでした。エラーは出ていません。

やり方を教えていただけると有り難いです。回答よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

0

ループを回して何度もDrawMeshを実行すればいいかと思いますが、それではうまくいかなかったでしょうか?

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

public class ExampleClass : MonoBehaviour
{
    Mesh mesh;
    Vector3[] positions;
    Quaternion[] rotations;
    int count;
    public Material material;
    public int rows = 100; // 行数
    public int columns = 100; // 列数

    private void Start()
    {
        mesh = new Mesh();
        mesh.vertices = new Vector3[] {
        new Vector3 (-1f, -1f, 0),
        new Vector3 (-1f,  1f, 0),
        new Vector3 (1f , -1f, 0),
        new Vector3 (1f ,  1f, 0),
    };

        mesh.uv = new Vector2[] {
        new Vector2 (0, 0),
        new Vector2 (0, 1),
        new Vector2 (1, 0),
        new Vector2 (1, 1),
    };

        mesh.triangles = new int[] {
        0, 1, 2,
        1, 3, 2,
    };

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

        count = rows * columns;
        positions = new Vector3[count];
        rotations = new Quaternion[count];
        for (var i = 0; i < rows; i++)
        {
            for (var j = 0; j < columns; j++)
            {
                var index = (i * columns) + j;

                // XY平面上に縦にrows個、横にcolumn個並ぶように配置する
                var position = new Vector3((j - ((columns - 1) * 0.5f)) * 2.0f, (i - ((rows - 1) * 0.5f)) * 2.0f, 0.0f);

                // それぞれの正方形の回転を決める
                // さしあたりランダムに少しだけ傾けてみました
                var randomAngle = Random.Range(-10.0f, 10.0f);
                var randomDirection = Random.insideUnitCircle.normalized;
                var rotation = Quaternion.AngleAxis(randomAngle, randomDirection);

                positions[index] = position;
                rotations[index] = rotation;
            }
        }
    }

    void Update()
    {
        for (var index = 0; index < count; index++)
        {
            Graphics.DrawMesh(mesh, positions[index], rotations[index], material, 0);
        }
    }
}

毎フレーム1万回のDrawMeshはけっこうな負荷になると思われます。もしマテリアルがGPUインスタンシングに対応していれば、代わりにDrawMeshInstancedを使うとレンダリングコストを大幅に削減できるかもしれません。

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

public class ExampleClass : MonoBehaviour
{
    Mesh mesh;
    Matrix4x4[][] matrices;
    int count;
    public Material material;
    public int rows = 100;
    public int columns = 100;

    private void Start()
    {
        mesh = new Mesh();
        mesh.vertices = new Vector3[] {
        new Vector3 (-1f, -1f, 0),
        new Vector3 (-1f,  1f, 0),
        new Vector3 (1f , -1f, 0),
        new Vector3 (1f ,  1f, 0),
    };

        mesh.uv = new Vector2[] {
        new Vector2 (0, 0),
        new Vector2 (0, 1),
        new Vector2 (1, 0),
        new Vector2 (1, 1),
    };

        mesh.triangles = new int[] {
        0, 1, 2,
        1, 3, 2,
    };

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

        // 各インスタンスの姿勢はMatrix4x4として与える
        count = rows * columns;
        var flatMatrices = new Matrix4x4[count];
        for (var i = 0; i < rows; i++)
        {
            for (var j = 0; j < columns; j++)
            {
                var index = (i * columns) + j;

                var position = new Vector3((j - ((columns - 1) * 0.5f)) * 2.0f, (i - ((rows - 1) * 0.5f)) * 2.0f, 0.0f);

                var randomAngle = Random.Range(-10.0f, 10.0f);
                var randomDirection = Random.insideUnitCircle.normalized;
                var rotation = Quaternion.AngleAxis(randomAngle, randomDirection);

                var scale = Vector3.one;

                flatMatrices[index] = Matrix4x4.TRS(position, rotation, scale);
            }
        }

        // 一度に描画できるインスタンスは1023個までなので、あらかじめそれを超えないよう切り分けておく
        matrices = flatMatrices.Select((m, i) => (m, i / 1023))
            .GroupBy(t => t.Item2)
            .Select(g => g.Select(t => t.Item1).ToArray()).ToArray();
    }

    void Update()
    {
        foreach (var m in matrices)
        {
            Graphics.DrawMeshInstanced(mesh, 0, material, m);
        }
    }
}

結果比較

追記

タイル1個ごとに頂点が4個生成されますので、頂点総数は130×130×4=67600個となります。頂点インデックスの形式がデフォルトの16ビット整数ですと65535個までしか扱えず、途切れてしまったものと思われます。
さしあたり、indexFormatRendering.IndexFormat.UInt32に切り替えてみてはいかがでしょうか?

    void GenerateMesh()
    {
        MeshData data = new MeshData(tiles);
        mesh = new Mesh();

        // 頂点インデックスのフォーマットを32ビット整数に変更する
        mesh.indexFormat = IndexFormat.UInt32;

        mesh.vertices = data.vertices.ToArray();
        mesh.triangles = data.triangles.ToArray();
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
        Graphics.DrawMesh(mesh, Vector3.zero, Quaternion.identity, material, 0);
    }


リファレンスによると一部の環境では32ビットインデックスをサポートしないそうですので、それらにも対応させたい場合はメッシュを複数個に分割する必要がありそうですね。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/10 05:41

    すいません、ご質問者さんの情報追記とかぶってしまいました。ご質問者さんの方法に沿った案が思いつきましたら、追って追記いたします。

    キャンセル

  • 2019/04/10 06:18

    Worldの修正案を追記しました。この方法は使えそうでしょうか?

    キャンセル

  • 2019/04/10 06:43

    mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;

    と書いたら、300×300でも描画することが出来ました。回答ありがとうございました。

    キャンセル

0

とりあえず、自分で書いたコードを載せます。ただ、widthとheightが130以上(?)だと中途半端にしか描画されません。

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

public class World : MonoBehaviour {

    public int width;
    public int height;

    public Tile[,] 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 Tile[width, height];

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

    void GenerateMesh()
    {
        MeshData data = new MeshData(tiles);
        mesh = new Mesh();
        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 MeshData{

    public List<Vector3> vertices;
    public List<int> triangles;

    public MeshData(Tile[,] 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(Tile 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 Tile{

    public enum Type { Stone }
    public Type type;

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


200×200で描画すると、中途半端にしか描画されません。なぜなのでしょうか? 100×100は普通に描画されます。

イメージ説明
イメージ説明

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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