プロジェクターで半透明オブジェクトに影を投影するときに一番問題となるのは、プロジェクターがどのようにして影を受け取るオブジェクトの透明度を知ることができるかということです。通常、透明度はテクスチャとカラープロパティーのアルファ成分から計算され、これらのプロパティーは影を受け取るオブジェクトのマテリアルが持っています。これをなんとかしてプロジェクターのマテリアルに渡してあげないといけないのです。

もし、単に透明度を無視して影を投影したい場合(例えばガラスに影を投影したい場合など)、描画順の問題セクションまで進んで構いません。

ARアプリケーションでは、目に見えない平面の上に影を落として影と現実の映像をブレンドしたいという要望があります。もしこのようなことをしたい場合は、目に見えない平面に影を落とすページを参照してください。

影を受け取るオブジェクトのプロパティーをプロジェクターのマテリアルに渡す (version 1.2.0から利用可能)

もし、たった一つのマテリアルが影を受け取る全てのオブジェクトに共有されているのであれば、そのプロパティーをプロジェクターのマテリアルにコピーするのは簡単ですが、複数ある場合はスクリプトを使ったとしてもそれは不可能になります。なぜなら、プロジェクターの影を描画している間にプロパティーの値を変更することができないからです。

幸いなことに、UnityにはMaterialPropertyBlockというのがあり、マテリアルを変更することなく、マテリアルのプロパティーの値を上書きする機能が備わっています。もし影を受け取るオブジェクトのレンダラーがMaterialPropertyBlockを持っていたら、それはレンダラーのマテリアルだけでなく、プロジェクターのマテリアルにも反映されるのです。

Dynamic Shadow ProjectorはこのMaterialPropertyBlockを利用してプロジェクターにオブジェクトの透明度を計算するためのプロパティーをプロジェクターに渡す、PropertyBlcockForTransparentReceiverというコンポーネントを提供します。

このコンポーネントは影を受け取る全ての半透明オブジェクトに追加する必要があります。もし、マテリアルにStandardシェーダー(ビルトイン RP)またはLitシェーダー(Universal RP)を使っている場合、デフォルトの設定のままで大丈夫です。

Default Settings for Standard Shader (Left) and URP Lit Shader (Right)

もしカスタムシェーダーを使っている場合は、カスタムシェーダーの仕様に合わせて設定を変更してください。

影を受け取るオブジェクトのプロパティーをプロジェクターのシェーダーで利用する (version 1.2.0から利用可能)

初期設定ではDynamic Shadow Projectorのシェーダーは影を受け取るオブジェクトのプロパティーを使わないようになっています。というのも、影を受け取るオブジェクトのプロパティーを使うには追加のコードが必要になるため、シェーダーが遅くなってしまうからです。そのため、影を受け取るオブジェクトのプロパティーを使うためには明示的に設定する必要があります。

プロジェクター用のマテリアルのインスペクタービューに、’Transparent Support’ という項目があるので、そこにチェックを追加してください。

Enable Receiver Properties

Enable receiver’s properties in material editor

次のテーブルはStandardシェーダーやLitシェーダーの設定に応じて、どこにチェックを入れる必要があるのかを示しています。

Item Name Rendering Mode (Standard) Surface Type (URP Lit)
Cutout Cutout Opaque + Alpha Clipping
Transparent Transparent / Fade Transparent

もしLitシェーダーで’Transparent + Alpha Clipping’を使っている場合は両方(Everything)にチェックを入れてください。

描画順の問題

もし不透明オブジェクトやCutoutのオブジェクトに影を投影するだけであれば、これ以上の問題はありません。しかし、半透明のオブジェクトに影を投影する場合には描画順の問題が残ります。

ビルトインレンダーパイプラインの場合、プロジェクターのマテリアルのRender Queueのデフォルト値は2999です。これは、プロジェクターが描画されるタイミングが、スカイボックスを描画した後、半透明オブジェクトを描画する前になるということです。これでは半透明オブジェクトを描画したときに影が上書きされてしまうので、Render Queueの値を3000以上にして、半透明オブジェクトの後に描画されるようにする必要があります。

しかし、全ての影が半透明オブジェクトの後に描画されるので、正しい描画順になるとは言えません。例えば、2つの半透明オブジェクトAとBがあって、Bの方に丸い影が投影されているとしましょう。もしオブジェクトAの方がカメラに近ければ、描画結果は下の図の左側のようにならなければいけませんが、実際の描画結果は右側のようになってしまいます。

Render Queueの値を

BのRender Queue < プロジェクターのRender Queue < AのRender Queue

となるようにすれば、左側のように描画することは可能ですが、半透明のオブジェクトはカメラからの距離によってソートされるべきなので、固定の順番を設定するのは良いことではありません。

もしオブジェクトBが他の全ての半透明オブジェクトよりも先に描画して良いのであれば、オブジェクトBのRender Queueの値を2998以下にして、プロジェクターはデフォルトの描画順で描画するのは悪くないでしょう。

Universalレンダーパイプラインの場合、プロジェクターのRender Queueの値は使われません。代わりにProjector For LWRPコンポーネントのRender Pass Eventプロパティーが使われます。デフォルト値はAfter Rendering Opaquesで、不透明オブジェクトの後、スカイボックスの前にプロジェクターが描画されます。これをAfter Rendering Transparentsにすれば半透明オブジェクトの後にプロジェクターが描画されるようになります。
また、Render Queue Lower/Upper Boundというプロパティーもあり、半透明オブジェクトに影を落とすためにはUpper Boundを3000以上の値にする必要があります。

Universalレンダーパイプラインでもビルトインレンダーパイプラインと同じように正しい描画順にはならないという問題が残ります。この問題を修正することは不可能ですが、影響を少し軽減することはできます。

Projector For LWRPにAdditionalProjectorRendererというコンポーネントがあり、プロジェクターを複数のマテリアルで複数回、異なるタイミングで描画することができます。次の図はこのコンポーネントを使った例です。

この例では、ProjectorForLWRPコンポーネントが不透明オブジェクトに影を投影していて、最初のAdditionalProjectorRendererコンポーネントがCutoutオブジェクトに影を投影しています。そして、2番目のAdditionalProjectorRendererコンポーネントが半透明オブジェクトに影を投影しています。それぞれ異なるマテリアルを使っていて、それぞれが影を投影するオブジェクトのタイプに最適化されています。詳しくはDynamic Shadow Projector Extension For LWRPに含まれているShadowsOnTransparentサンプルシーンをみてください。

Links

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

Anti Spam Code *