■webプレイヤー
https://dl.dropboxusercontent.com/u/18978109/DrawMeshTest/DrawMeshTest.html
Unity公式のフォーラムで大量のメッシュを描画する方法として有効だというはなしのGraphics.DrawMesh命令を簡単に解説します
Unityの新しいバージョンでプロパティブロックまわりが改良されたそうなので プロパティブロックを使用してサンプルを作成してみました
< どこが新規に変わったのかいまいちわからないのですが >
ちなみに情報としては古めなので すでにほかのところに詳しい記事があるかもしれませんが、ここの管理人は怠けものなので国内の記事はあまり見ていません。
■Graphics.DrawMesh
● static function DrawMesh(mesh: Mesh, position: Vector3, rotation: Quaternion, material: Material, layer: int, camera: Camera = null, submeshIndex: int = 0, properties: MaterialPropertyBlock = null): void;
● static function DrawMesh(mesh: Mesh, matrix: Matrix4x4, material: Material, layer: int, camera: Camera = null, submeshIndex: int = 0, properties: MaterialPropertyBlock = null): void;
【パラメータ】
mesh :描画するメッシュデータ。
position :メッシュの位置。
rotation :メッシュの回転。
matrix :メッシュの変換行列(位置、回転、その他の変換)。
material :マテリアルを指定する。
layer :レイヤを使用する。
camera :デフォルトはNULL。メッシュはすべてのカメラで描画されますが 指定がある場合指定されたカメラでレンダリングします。
submeshIndex :描画するメッシュのどのサブセット。これは、複数の材料で構成されたメッシュに適用される。
properties :メッシュが描画される直前にマテリアルにプロパティ変更を適用します。※MaterialPropertyBlock
DrawMesh命令は1フレームごとにメッシュを描画します。通常のゲームオブジェクトと同様にシェーディングされ影が落ちてプロジェクターの影響を受けることができます。デフォルト状態ではすべてのカメラにレンダリングされますが特定のカメラを指定することもできます。
ゲームオブジェクトの作成と管理のオーバーヘッドを避けて大量のメッシュを描画したい場合DrawMesh命令は有効に機能します。DrawMeshはすぐにメッシュを描画せず Updateの後にレンダリングパイプラインにデータを送り、そのあと通常のレンダリングプロセスの一部としてレンダリングされます。 すぐにメッシュを描画したい場合は、Graphics.DrawMeshNowを使用します。
DrawMeshはすぐにメッシュの描画がされないため、関数の呼び出しの時に材料特性を変更すると、メッシュはそれらをピックアップすることはありません。あなたは、同じマテリアルを用いてメッシュのシリーズを描きたいのですが、すこしだけマテリアルのプロパティ(例えば色を変更)をする場合は、MaterialPropertyBlockのパラメータを使用します。
■MaterialPropertyBlock
MaterialPropertyBlockはGraphics.DrawMeshによって使用されているRenderer.SetPropertyBlockです。、同じマテリアルで複数のオブジェクトを描画するような場合、たとえばメッシュの色を変更したい場合シェーダ内のプロパティの値を部分的に変更することで対応します。
【機能】
AddColor : カラープロパティを追加します。
AddFloat :フロート型プロパティを追加します。
AddMatrix : マトリックス型プロパティを追加します。
AddTexture :テクスチャ型プロパティを追加します。
AddVector :ベクター型プロパティを追加します。
Clear : プロパティ値のクリア。
GetFloat : プロパティブロックからフロートを取得します。
GetMatrix : プロパティブロックから行列を取得します。
GetTexture : プロパティブロックからテクスチャを取得します。
GetVector : プロパティブロックからベクトルを取得します。
【Example】
|
public class XXXXX : MonoBehaviour private MaterialPropertyBlock _PropertyBlock;
void Start() _PropertyBlock = new MaterialPropertyBlock(); } void Update() _PropertyBlock.Clear(); } }
|
PropertyBlock を効率よく使用するためには1つのブロックを作成してDrawMeshをコールするたびにブロックを再利用する方法です。
Graphics.DrawMesh()の前に使用ブロックをクリアし AddXXX を使用して値を追加します。
指定できるプロパティは使用しているシェーダ内で登録されているプロパティとなります。 複数のシェーダを使用する場合はShader.Find(シェーダ名)で書き換え対象のシェーダを指定する必要があります。
●DrawMeshManager.cs
| using System.Collections.Generic; public class DrawMeshManager : MonoBehaviour Objs.Add(newObj); Vector3 playerPosition = Player.position; |
●ObjData.cs 【構造体クラス】
| using System; public ObjData(int ObjType, Vector3 worldPosition, Quaternion rotation) _Color = new Color(1.0f,0.2f,0.05f); public Vector3 WorldPosition |
いままでは配列で管理して結合した後GPUに転送という方法が知られていましたが それにに比べると メモリ消費も少なくて なにより頭を悩ませることがないのがいいですね。
画像のStatisticsで表示されるDrawCall数は3000を越えていますが FPS値はふつうに高いままなので Unityの中身はわからないので想像ですが、おそらくCPU側でGraphics.Draw関数をコールした回数だと思われます。VRAMへの描画は1フレームで行われているらしいです。
左のGUIのFPS表示はエディタから実行した数値です。 PCの環境にもよりますがwebプレイヤーでは60フレーム程度はでていると思います。
Unityのツリーシステムなどでは メモリ使用量はモデルの頂点数分の合計となるためかなり大きめな数値となっています おそらくは内部でメッシュをマージして転送する方法を行っているのだと思いますが、今回のDrawMesh関数を使用するサンプルはアップデート内部でシェーダにモデル1つ分のデータを送るだけなのでGPU側のメモリはほとんど消費されていません
それからスキンモデルにもDrawMwshを使用してみたいところなのですが現時点ではスキンウェイトのモデルはダイナミックバッチがかからない仕様になっているそうなのです
スキニングのフィルターがブラックボックスなので調べてみないとわかりませんが、アニメーションするキャラクターを大量に配置したい場合はスキンウェイト用のシェーダを自作する必要があるかもしれません。















