プロジェクターが遅い理由
プロジェクターのレンダリングは遅いです。
なぜなら、投影される影の大きさに関わらず、レシーバーのポリゴン全体がプロジェクター毎に繰り返し描画されるからです。
Basicサンプルを例に取ると、3つのプロジェクターと1つの平面のレシーバーがあります。
このシーンを描画するときは、平面オブジェクトが4回描画されることになります。1回は平面オブジェクトそのものをレンダリングするため、3回は3つのプロジェクターそれぞれをレンダリングするためです。
もし、影が投影されているエリアだけを描画することができれば、パフォーマンスは大きく改善されるはずです。
これが Fast Shadow Receiver のやっていることです。
パフォーマンスチェック
Fast Shadow Receiver を導入する前に、プロジェクターのレンダリングが本当にパフォーマンス低下の原因になっているかチェックしましょう。
もし、プロジェクターのフラグメントシェーダーだけを無効にできれば、プロジェクターのレンダリングにどれだけ時間がかかっていたのかを見積もることができます。
Assets/DynamicShadowProjector/Shaders/Projector フォルダーの中に ‘Invisible’ というマテリアルがあり、このマテリアルをプロジェクターに使えば、プロジェクターのフラグメントシェーダーが実行されることはなくなります。Projector For LWRP を使っている場合、’Invisible’ マテリアルを Projector Renderer Feature の ‘Replace Projector Material For Debug’ プロパティーに割り当てることで全てのプロジェクターのマテリアルを置き換えることができます。
このプロパティーはデバッグモードでのみ有効なので、デバイスでテストするときは Development Build でビルドしてください。
ビルトインパイプラインを使っている場合は、プロジェクターのマテリアルを一つ一つ ‘Invisible’ に置き換えてください。
パフォーマンスを見るために、Assets/DynamicShadowProjector/Samples フォルダーの中にある FPSCheck プレハブを使うことができます。必要に応じてフォントのサイズや色を変更することもできます。
このサンプルシーンを古いアンドロイドデバイスでテストしました。GPU は Adreno 305 でスクリーンの解像度は 960 x 540 です。プロジェクターシェーダーの影響を強めるために、平面オブジェクトにスケールをかけて、カメラのアングルも少し変えています。
プロジェクターのレンダリングが有効な時の FPS はだいたい 52 でした。一方、プロジェクターのレンダリングを ‘Invisible’ マテリアルで無効にした時の FPS は 60 でした。このことから、プロジェクターのレンダリングがパフォーマンスに影響していたことがわかるので、Fast Shadow Receiver を導入することでパフォーマンスを改善することができそうです。
Fast Shadow Receiver を導入する
まだ Fast Shadow Receiver を持っていなかったら、Asset Store から購入してインストールしてください。
Fast Shadow Receiver を使うのは簡単で、’Easy Setup WizardでProjector Managerをセットアップする‘ の手順に従うだけでいいのですが、説明をより明確にするためにここでもデモンストレーションしたいと思います。
その前に、Fast Shadow Receiver をただの平面に適用するだけでは面白くないので、シーンにいくつかオブジェクトを追加します。
シーンに空のゲームオブジェクトを追加して、’Environment’ という名前をつけました。そのオブジェクトの下に平面オブジェクトを移動して、いくつかキューブオブジェクトを追加します。これらのメッシュオブジェクトは Static であることが前提で、通常、ライティングの結果をライトマップにベイクして使用します (Fast Shadow Receiver 自体は Static になっていることを要求しませんが、’Environment’ オブジェクトからの相対位置がゲーム実行中に変化してはいけません)。
さて、Fast Shadow Receiver を ‘Environment’ オブジェクトに適用してみましょう。
そして、そのレイヤーの名前を ‘Environment’ としておきます。
‘Environment’ オブジェクトとその子オブジェクトのレイヤーを全て ‘Environment’ に変更します。
Fast Shadow Receiver オブジェクトを作成します。基本的な考え方は、プロジェクター毎にレシーバーオブジェクトを作成して、それぞれのレシーバーオブジェクトが割り当てられたプロジェクターの影を受け取って描画する、ということになるのですが、プロジェクター毎にレシーバーオブジェクトを作成するというのは少々厄介です。特に、プロジェクターがプレハブからインスタンス化されて作成される場合。そのような場合のために、Projector Manager を使います。Projector Manager はレシーバーオブジェクトのひな型を保持し、プロジェクターがアクティブになるたびにひな型を複製してレシーバーオブジェクトを作成します。
まず、空のゲームオブジェクトを作成して ‘Projector Manager’ と名前をつけます。そして、’Scripts > FastShadowReceiver > Projector Manager’ コンポーネントを追加します。
Inspector ビューにある ‘Easy Setup Wizard’ ボタンを押します。
‘Create a New Shadow Receiver’ を押して新しいレシーバーオブジェクトのひな型を作成したら次のページに進みます。
次のページでは、’Root Object’ に ‘Environment’ オブジェクトを、’Environment Layers’ に ‘Environment’ レイヤーをセットします。
Environment Layers はプロジェクターが Environment オブジェクトを無視するのに使われます。プロジェクターの影はレシーバーオブジェクトが代わりに受け取ることになります。
‘Create a New Mesh Tree’ ボタンを押します。
Mesh Tree はアセットデータで、Environment オブジェクトのメッシュデータをツリー構造にして保存します。これによって、レシーバーがプロジェクターと交わっているポリゴンを素早く検索できるようになります。
Mesh Tree は、メッシュデーターを集めるために Environment オブジェクトへの参照を必要とします。シーンオブジェクトの参照を使うことはできますが、アセットデータはシーンオブジェクトの参照を保持することができないため、Mesh Tree を再度ビルドするときにはもう一度シーンオブジェクトの参照を指定する必要があります。ですので、Environment オブジェクトのプレハブを作成して、プレハブの参照を利用することをお勧めします。
‘Create Mesh Tree’ ボタンを押して、Mesh Tree を保存する場所を指定してください。すると次のウィンドウが現れます。
Mesh Tree には半透明ポリゴンを含めてはいけません。もし、半透明ポリゴンにも影を投影したいのであれば、半透明のオブジェクトに違うレイヤーを割り当てて、プロジェクターから直接影を投影するようにしてください。
‘Build’ ボタンを押してウィザードのページに戻ります。次のページではレシーバーに割り当てるレイヤーを選択します。レシーバーはプロジェクター毎に複製されるわけですが、レイヤーが同じだと他のプロジェクターの影も受け取ってしまうため、できるだけ重複しないようにレイヤーを割り当てる必要があるのです。そのため、ここではできるだけ多くのレイヤーを選択してください。’Nothing’ にしておけば、名前のついていない全てのレイヤーが使われます。
次に、’AutoProjector’ コンポーネントを全てのプロジェクターオブジェクトに追加します。まずはプレハブに含まれているプロジェクターです。プレハブアセットを検索するフォルダーを指定して、’Search for Prefab’ ボタンを押すと、プロジェクターを含むプレハブのリストが表示されます。リストの右端にあるチェックボックスにチェックを入れることで、AutoProjector コンポーネントをプレハブに追加することができます。
これでほぼ設定は完了です。最後のページでは Fadeout Distance を指定します。もしたくさんのプロジェクターがあって、カメラから遠いプロジェクターを消してしまいたい場合は、その距離を指定してください。常にプロジェクターを表示しておきたい場合はカメラのファークリップよりも大きな値を指定してください。
Dynamic Shadow Projector を使っている場合、Raycast Plane Mask も Infinite Plane も使うことはできないので、これで設定は終了です。
結果
Fast Shadow Receiver を導入することによって、FPS は 52 から 59 に改善されました。