質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.22%

Unity Universal RP における影のみの描画とレイヤーを用いた透過

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 681

octa_gongon

score 5

前提

Unity の Universal RP を用いてARアプリケーションを制作しようとしていて、次の2つの処理を実装したいのですが、シェーダについての理解が追いついておらず、うまくやる方法が見つかりません。知恵をお貸しいただけると幸いです。

実現したいこと

1. 受けた影だけを描画するシェーダ

ARなので、影だけを描画したいのですが、URPに対応したシェーダでのやり方がわかりません。
このページで紹介されているカスタムノードを用いてシャドウのマップを取得することはできており、PBR Graph を用いて影の落ちた平面の影部分を透過することはできるのですが、透過を逆転させると影の落ちている部分と落ちていない部分の区別ができなくなります。(透過してしまった部分は影を受けられないから、という理解で合っているでしょうか…?)
影だけ透過するシェーダ
影だけ透過するシェーダ

2. 透明マント

ハリーポッターの透明マントの要領で、自身が透明かつ自身よりも奥のオブジェクトをすべて透過する処理を実装したいです。このページを参考にいろいろいじってみたところ、一応はできました。(なぜか記事の通りにやっても同じことができなかったのですが、透明マントレイヤーと通常オブジェクトでレイヤーを分けてどちらもDefault Layer Maskから外したうえで、マント→オブジェクトの順でRender Features を用いて追加の描画をしています。ここでマントに透過オブジェクトを用いれば図のように処理できました。)
イメージ説明

しかし、Default Layer Maskから対象を外してしまった時点でオブジェクト同士のシャドウが描画されなくなってしまうのが気に入りません。先ほどの記事のように遮蔽部分の描画色を変えるだけならDefault Layer Maskから外さずにシャドウを維持して行えたのですが、この処理で遮蔽部分のシェーダに透過シェーダを適用すると下にもともとのオブジェクトが描画されているため思ったような表示になりません。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

「受けた影だけを描画するシェーダ」について

下図のように、マスターノードの設定を乗算モードにしてやるというのはどうでしょうか?
ついでに、影の色を調節できた方がいいかと思ってプロパティを用意し、そのRGBは影にスクリーン合成、さらにAに応じて白色を乗せてやり影を薄めるようにしてみました。

図1

マスターノードですが、すみませんが勉強不足につきPBRマスターが内部で何をしているか分からず、余計な色を乗せてしまうかもしれないと不安になりまして、PBRよりも素直な色を描いてくれるだろうと期待してUnlitマスターを使用しました。
ですがこれだとシェーダーグラフのカスタムライティング:Unity 2019 でグラフを拡張する - Unity Technologies Blogのやり方そのままでは正しくShadowAttenを得ることができず、Unity2019.3 URP ShaderGraph Unlit マスターで影を受けるの方法に基づいてライト情報取得ノードのコードを下記のように変更しました。

#if SHADERGRAPH_PREVIEW
   Direction = half3(0.5, 0.5, 0);
   Color = 1;
   DistanceAtten = 1;
   ShadowAtten = 1;
#else
#if SHADOWS_SCREEN
   half4 clipPos = TransformWorldToHClip(WorldPos);
   half4 shadowCoord = ComputeScreenPos(clipPos);
#else
   half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
#endif
   Light mainLight = GetMainLight();
   Direction = mainLight.direction;
   Color = mainLight.color;
   DistanceAtten = mainLight.distanceAttenuation;
   ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
   half4 shadowParams = GetMainLightShadowParams();
   ShadowAtten = SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), TransformWorldToShadowCoord(WorldPos), shadowSamplingData, shadowParams, false);
#endif

「透明マント」について

当初はレンダリングパイプラインのカスタマイズで何とかしようと思ったものの、ご質問者さんのおっしゃる影問題をどうにも解決できずあきらめました...もしかするとパイプラインの途中にちょこっと追加処理を挟むだけでは困難で、レンダリングパイプラインを完全に自作してやる必要があるかもしれません。
代替案として、カメラをもう一つ用意して背景だけを描かせ、メインのカメラのレンダリング時にはそれを透明マントの表面に貼り付ける方針を検討してみました。

まずシーン上にあるオブジェクトのうち、透明マントを着たオブジェクト(ご質問者さんの想定における薄紫色のキューブ)と透明マントを通すと見えなくなるオブジェクト(ご質問者さんの想定における不透明キューブ)のレイヤーをともに「InvisibilityCloakEffect」と名前を付けたレイヤーに設定しました。これ以外のレイヤーにあるものは透明マントを通しても見ることができるという想定です(背景オブジェクトなど)。
メインカメラの子としてもう一つカメラを作り(メインカメラを動かした際に追従して動かしたかったため)、下図のようにPriorityを-2、Output Targetを適当なレンダーテクスチャとしました。Culling MaskからはInvisibilityCloakEffectレイヤーを除外しています。
なお、もしおっしゃるように本当に自身よりも奥のオブジェクトをすべて透過する...つまり地面すら貫通して背景が見えるようにしたい場合はレイヤー分けすら不要で、Culling MaskをNothingにしてしまえばいいでしょう(ARとのことですので、背景は実写映像であって地面は不要ということでしょうか)。
Audio Listenerもメインカメラのものがあるので外しました。

図2

メインカメラ側は特に設定はいじっていません。

図3

透明マント用マテリアルは下図のような構成にしました。テクスチャプロパティとして「Albedo」と「Background」、色プロパティとして「Color」を作り、Albedoからサンプリングした色とColorを掛け、RGBにはAを乗算して暗くしてからアルベド色として出力しました。
Backgroundからはスクリーンスペース座標を基に色を取ってきて、アルファに応じて強度を落としてからエミッションに接続しました。

図4

それを使ったマテリアルを作り、Backgroundにはサブカメラのレンダリング先となるレンダーテクスチャをセットしておきます。ColorやAlbedoを調整すると色を乗せることができ、それらのアルファが小さいほど背景色が優位となって透けていきます。

図5

この結果、下図のような見た目になりました。
奥にある青玉、半透明の赤玉、市松模様の床はサブカメラのレンダリング対象になっているので透明マントを通して見えるのに対し、白と灰色のキューブは透明マントによって隠されます。透明マントを着た中央の紫キューブは実際のところ不透明オブジェクトとして扱われていますので、影を落としたり影を受けたりすることが可能です。

図6

透過部分の映像はサブカメラに依存していますので、シーンビューでは背景がずれて表示されてしまう欠点がありますが、ゲームビュー上ではまともに見えるかと思います。この方式の利点としては、透明部分の見え方を透明マントマテリアル側でコントロールしやすいというのが挙げられるかもしれません。たとえばBackgroundテクスチャをサンプリングする際にサンプリング座標を適当に変調させてやれば、プレデターみたいに背景が歪んでかすかに存在を察知できる...みたいな表現もできそうです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/07 21:32

    ありがとうございます…!手元の環境でも再現をすることができました。render texture によるこうした手法は初めて知ったので感動しております
    重ねて申し訳ないのですが、例えば影のみのシェーダをCubeに適用すると光が当たっていないはずの側面にも影が出てしまう(Shadow atten が影を受ける向きを考慮していない…?)のですが、これを解決する方法はないでしょうか…

    キャンセル

  • 2020/02/07 22:52

    すみません、これに関しては自己解決しました

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる