1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Unity】SetPropertyBlockメソッドの挙動詳細

Posted at

はじめに

ソースコードからRenderer系コンポーネントに設定されたMaterialのプロパティを設定する場合、主に以下の三つの方法があります。

  1. sharedMaterialsプロパティやsharedMaterialプロパティで取得したMaterialにプロパティを設定する
  2. materialsプロパティやmaterialプロパティで取得したMaterialのコピーにプロパティを設定する
  3. Rendererクラスが持つSetPropertyBlockメソッドを使用して設定する

一つ目の方法は、Materialのコピーは発生しないためメモリ使用量は一番少なくなります。**しかし、同じMaterialを参照している他のRendererにも変更が反映されてしまうため、使用できる場面は限られます。**この理由から、sharedMaterials系プロパティで取得したMaterialに対するプロパティの変更は公式ドキュメントで非推奨とされています1

二つ目の方法は、同じMaterialを参照している他のRendererが存在する場合にMaterialが丸ごとクローンされるため、他のRendererに変更が反映されてしまうことはありません2。Materialがクローンされる分メモリの使用量は多くなります。

三つ目の方法は、Material自体のプロパティを変更することなくプロパティを上書きする方法です。Material全体をクローンする必要がないので、メモリ使用量の面で二つ目の方法よりも効率的になります3。少数のパラメーターを変更するだけの場合は、この方法を使用することが公式ドキュメントで推奨されています3

本記事では三つ目の方法について扱います。

SetPropertyBlockメソッドには、Renderer単位で設定するper-rendererな設定方法とMaterial単位で設定するper-materialな設定方法の二種類が存在します3。これらの設定方法を併用した場合にどうなるのか、PropertyBlockが設定されているかどうかを取得するHasPropertyBlockメソッドはこれらの設定方法によって挙動が変わるのか、そのあたりが少しわかりづらく感じました。
本記事ではそのあたりについて調査してまとめています。

検証環境

  • Unity 2020.3.12f1

まとめ

急いでいる人向けにまとめを最初に書いておきます。

per-renderとper-materialの両方で設定した場合は、per-materialが優先されます。優先の判断はMaterial単位で行われます。
HasPropertyBlockメソッドはper-renderかper-materialかに関わらず、PropertyBlockが設定されていればtrueになります。

per-renderの設定 per-materialの設定 反映される方 HasPropertyBlock()
してない してない なし false
した してない per-render true
してない した per-material true
した した per-material true

挙動詳細

以下の四つの場合について調べました。

  • なにも設定しない場合
  • per-renderで設定した場合
  • per-materialで設定した場合
  • per-renderとper-materialで同時に設定した場合

視覚的にわかりやすいよう、左半分にインデックス0のMaterial、右半分にインデックス1のMaterialを割り当てた球を用意し、それぞれの場合にプロパティの変更がどのように反映されているか例として示しています。インデックス0のMaterialも1のMaterialも標準色は白です。

なにも設定しない場合

HasPropertyBlock()はもちろんfalseになります。

なにも設定していないので、左右どちらも標準の白色になっています。
default.jpg.png

per-renderで設定した場合

引数にMaterialインデックスを渡さない場合は、Renderer全体のスコープでプロパティが設定されるため、すべてのMaterialに反映されます。
HasPropertyBlock()trueになります。

以下のようなコードでper-renderで赤色を設定すると、全てのMaterialに反映されるため、インデックス0と1両方のMaterialに色が反映され、全体が赤くなります。

per-render.cs
var mb = new MaterialPropertyBlock();
mb.SetColor("_Color", Color.red);
renderer.SetPropertyBlock(mb);

per-render.png

per-materialで設定した場合

引数にMaterialインデックスを渡した場合は、Materialのスコープでプロパティが設定されるため、指定したインデックスのMaterialにのみ反映されます。
HasPropertyBlock()trueになります。

以下のようなコードでインデックス0のMaterialのみに緑色を設定すると、インデックス0のMaterialにのみ反映されるため、左側が緑になります。

per-material.cs
var mb = new MaterialPropertyBlock();
mb.SetColor("_Color", Color.green, 0);
renderer.SetPropertyBlock(mb);

per-material.png

per-renderとper-materialで同時に設定した場合

per-materialが優先して使われます。
この時に、優先度の判断はプロパティごとではなくMaterialごとに行われます。
つまり、per-renderとper-materialの両方を設定している状態で、per-renderで定義されていてper-materialで定義されていないプロパティがある場合、そのプロパティはper-materialのPropertyBlockが参照されて定義されていないものとして扱われます。

HasPropertyBlock()trueになります。

以下のようなコードでper-renderで赤を、インデックス0のMaterialに緑色を設定すると、インデックス0のMaterialにはper-renderとper-materialの両方が設定されますが、per-materialが優先されるため左側が緑になります。インデックス1のMaterialにはper-renderのみが設定されているので、右側が赤になります。

per-material.cs
{
	var mb = new MaterialPropertyBlock();
	mb.SetColor("_Color", Color.red);
	mb.SetFloat("_Metallic", 1);
	renderer.SetPropertyBlock(mb);
}
{
	var mb = new MaterialPropertyBlock();
	mb.SetColor("_Color", Color.green);
	renderer.SetPropertyBlock(mb, 0);
}

per-render-and-material.png

おしまい

  1. Unity APIリファレンス「Renderer.sharedMaterials」

  2. Unity APIリファレンス「Renderer.materials」

  3. Unity APIリファレンス「Renderer.SetPropertyBlock」 2 3

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?