土屋つかさの技術ブログは今か無しか

土屋つかさが主にプログラミングについて語るブログです。

#unity 小ネタ:実行中にマテリアルを複製する

 ネットで検索してスパッと記事が見つけられなかったのでメモっておく小ネタシリーズ。

 「実行中にマテリアルを生成したい」「可能ならアセットから読み込んだマテリアルをベースに一時的なコピーを作りたい」という状況に遭遇しました。
 なんでコピーを作りたいかというと、複数のメッシュが同じマテリアルを使用する場合、そのマテリアルのパラメータを更新したら全てのメッシュが影響を受けてしまい、これを避けたかった為です。
 それで、マテリアルを複製する方法を探してたんですが、考えてみればこれでいいじゃんと気づきました(baseMaterialがコピー元のマテリアル)。

//マテリアルをコピーする
var CopvMaterial = new Material(baseMaterial);

 MaterialのコンストラクタにはShaderオブジェクトを受け取る物とMaterialオブジェクトを受け取る物の2種類があり、後者を使えばパラメータもコピーしてくれます*1
 MaterialクラスはC++ネイティブコードで実装されていて、この初期化処理もC++側で行われています。パフォーマンスは計測してないので不明ですが、そんなに遅い事はしていないと信じたい。いやでもどうかな……(愚直にプロパティを検索→巡回してコピーしてたら相当遅いと予想される。それでもC#でやるよりは早いと思いたいけども)。
 余談ですが、複数のマテリアルを使うとレンダリング時のバッチングが期待出来ないので、可能な限りマテリアルは共有した方が良い筈です。一時的にこのような処理が必要になったとしても、終わったら再度共有化した方が効率的です(多分)。

追記

 ネット経由でお友達のエンジニアさんから「その方法パフォーマンス的に相当厳しいからちゃんと計測した方がいいよ」とアドバイスを貰いました(ありがとう!)。うーん、やっぱり遅いかー。遅いよなー。
 ここでやろうとしていたのは、文字列を文字単位で制御する時に、それぞれに個別のマテリアルを設定できたら制御が楽だろう、という話だったんですが、横着せずに真面目に組んだ方が良さそう。

*1:前者の場合は受け取ったシェーダーをベースにパラメータが初期化されます