2012年08月31日

[Unity] DrawCallメモ(SetPropertyBlock())

以前、マテリアルの色(renderer.material.color)をプログラムで変えるとDrawCallが増えるという記事を書きました↓
[Unity] DrawCallメモ(transform.renderer.material.color)

それについてコメントをいただいたので検証してみました。
いただいたコメントは、
「マテリアルを複製しないでマテリアルのパラメータを変更するには SetPropertyBlock() を使う」
というもの。

結論を先に言うと、
「DrawCallの検証は途中までで、SetPropertyBlock()の使い方がわかった」
という記事になります。


はじめに、状況確認。

スフィアを4つ用意して↓
20120831drawcall001.png

各々に同じマテリアル(Diffuse)を適用します↓
20120831drawcall002.png

次に、スクリプトを実行するために空のゲームオブジェクトを用意しました。
検証するスクリプトは2つなので、各々チェックボックスで切替えて実行します↓

20120831drawcall003.png

準備ができたので検証に入ります。

@これまでやっていたマテリアルの切替え
MaterialTest1.js
---------------------------------------------------------------------
// これまでの手法(DrawCallが増加するはずだったのに……。3.5.5から解消?)
#pragma strict
private var objA : GameObject; // 各GameObjectのShaderには、マテリアル(Diffuse)が適用されている
private var objB : GameObject;
private var objC : GameObject;
private var objD : GameObject;

function Start() {
// GameObjectをキャッシュ
objA = GameObject.Find("TestSphereA");
objB = GameObject.Find("TestSphereB");
objC = GameObject.Find("TestSphereC");
objD = GameObject.Find("TestSphereD");
}

function Update() {
objA.transform.renderer.material.color = Color.green;
objB.transform.renderer.material.color = Color.white;
objC.transform.renderer.material.color = Color.cyan;
objD.transform.renderer.material.color = Color.blue;
}
---------------------------------------------------------------------
そしてDrawCallを確認。

プレビュー前(DrawCall = 4)↓
20120831drawcall005.png

プレビュー中(DrawCall = 4)↓
20120831drawcall004.png

あれ?DrawCallが増えるかと思いきや、増えてない……。
rendererのmaterialを触ってしまうとマテリアルの複製がされるということですが、
どこを見るとマテリアルの数がわかるんだろう……。


詳しいことはわからないまま、次の検証へ。
SetPropertyBlock() を使ってみます。
MaterialTest2.js
---------------------------------------------------------------------
// 参考
// http://docs.unity3d.com/Documentation/ScriptReference/Renderer.SetPropertyBlock.html
// http://docs.unity3d.com/Documentation/ScriptReference/MaterialPropertyBlock.html
// http://docs.unity3d.com/Documentation/ScriptReference/MaterialPropertyBlock.AddColor.html

#pragma strict
private var objA : GameObject; // 各GameObjectのShaderには、マテリアル(Diffuse)が適用されている
private var objB : GameObject;
private var objC : GameObject;
private var objD : GameObject;
private var materialProperty : MaterialPropertyBlock; // MaterialPropertyBlock

function Start() {
// GameObjectをキャッシュ
objA = GameObject.Find("TestSphereA");
objB = GameObject.Find("TestSphereB");
objC = GameObject.Find("TestSphereC");
objD = GameObject.Find("TestSphereD");
// MaterialPropertyBlockを生成
materialProperty = new MaterialPropertyBlock();
}

function Update() {
// マテリアルの色を設定
materialProperty.Clear();
materialProperty.AddColor("_Color", Color.green);
// GameObjectに反映
objA.transform.renderer.SetPropertyBlock(materialProperty);

// 他Objectも同様
materialProperty.Clear();
materialProperty.AddColor("_Color", Color.white);
objB.transform.renderer.SetPropertyBlock(materialProperty);

materialProperty.Clear();
materialProperty.AddColor("_Color", Color.cyan);
objC.transform.renderer.SetPropertyBlock(materialProperty);

materialProperty.Clear();
materialProperty.AddColor("_Color", Color.blue);
objD.transform.renderer.SetPropertyBlock(materialProperty);
}
---------------------------------------------------------------------
今度はどうなったかな?

プレビュー中(DrawCall = 4)↓
20120831drawcall004.png

こちらは予想通りDrawCallは増えず。
1つ目の検証と同様に、マテリアルが複製されているかどうかを知りたいところ。

ということで、SetPropertyBlock() を使ってGameObjectの色を変更することができました。

DrawCallを調べるという検証はマテリアルの数を調べるところで止まってしまいましたが、
SetPropertyBlock()の使い方がわかってよかったです(言い訳

------------------------- 2012.9.13追記 -------------------------
いただいたコメントのとおり、従来の方法(MaterialTest1.js)だと
Inspectorウィンドウのマテリアル名の後ろに(Instance)が表示されました↓

20120913drawcall001.png

なるほど。
これでマテリアルが複製されることをチェックできるようになりました。

SetPropertyBlock()については、複製はしないけどDrawCall数を抑えるものではないとのこと。
詳しい内容はコメントの通りということで、また1つ勉強させていただきました。

ありがとうございます!
posted by be-style at 15:41| Comment(2) | Unity
この記事へのコメント
お世話になっております。SetPropertyBlockについて検証していただき、ありがとうございます。

さて、SetPropertyBlockですが、あくまで「マテリアルを複製せずにシェーダのパラメータ転送が行える」ものなので、DrawCall数を抑えるものではないようです。
DrawCall数に変わりはありませんが、シーンの再生中に無駄なマテリアルのコピーが発生しないのと、シェーダプロパティの変数名ではなく、シェーダプロパティIDを指定することが出来るので、マテリアルの複製よりは高速に動作すると思われます。

また、マテリアルが複製されているかどうか?ですが、Inspectorウィンドウのマテリアル設定フィールドのマテリアルの名前の後ろに(Instance)が付いているとマテリアルが複製されているということみたいです。
シーン内の複製されているマテリアル数をカウントするツールを作ってみようかと思いましたが、renderer.material.nameプロパティにアクセスした時点でマテリアルが複製されてしまうので断念しましたw

それでは。
Posted by KTA at 2012年09月09日 01:10
詳細に教えていただきありがとうございました。
従来の記事に追記をして補足しました。
わざわざ検証方法まで検討していただいて、ありがとうございます!

マテリアルの複製は抑えても、DrawCall数は変化ないんですねー。
まだまだ知らないことが多いUnityですが、これからもコツコツ情報を蓄積していきたいと思います!
Posted by be-style at 2012年09月13日 21:02
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]