見出し画像

VRChatワールド制作における『Layer』の役割

2023/06/09 VRChat公式からレイヤーの公式マニュアルが公開されたので、その内容に沿って加筆修正しました。

今月のワールド製作者向け記事のお題は『Layer(レイヤー)』についてです。

『Layer』は、よくワールド制作の初心者さんから
『アバターの明るさがちがうんですが……』とか『ワールドが重たくて……』とかいう話が出るたびに割と説明する頻度の多い話題です。
なので、初心者さんには是非読んでほしい内容ですね。

結構まとめられている記事は多いので、書く必要性はあんまりないかなと思っていたのですが、検証したいことを思い出したので、そのついでにまとめてみました。

『Layer』とは

Unityにおけるレイヤーは、グループ分けのようなものです。

グループ分けをしておくと何が良いかというと、ある特定の効果を反映するか・しないかをグループごとにコントロールすることが出来ます。

このグループ分けは各ゲームオブジェクトに対して行われます。

レイヤーは0番目から31番目まであって、そのいずれかが必ずゲームオブジェクトに割り振られています。
初期状態では0番目の「Default」レイヤーに、ゲームオブジェクトが所属しています。

レイヤーが関係することは色々あるのですが、具体的な例を挙げると、
一つの光源があったとして、その光源によって明るくなる・明るくならないをレイヤーごとに決めることが出来ます。

例えばこんな感じに

恐らく、『これの何が便利なの?』とピンと来ていない人もいると思うので、この具体例で嬉しいポイントを書き出すと以下の通りです。
①光が当たる・当たらないをオブジェクトごとに制御できる
②影が出来る・出来ないをオブジェクトごとに制御できる
③光源の影響を受けないオブジェクトは、光源の計算が要らなくなるので、軽量化に繋がるケースがある

①、②は比較的想像しやすいと思いますが、実はレイヤーを上手く使うことで、③のように軽量化にもつながったりします。

レイヤーは他にも色々な機能に絡めて、使用されているので、それらについてもこの後の文章で詳しく説明していきたいと思います。

レイヤーに関係しているもの

レイヤーに関係するものを思いつく限り書き出すと以下の通りです。
①カメラの描画設定
②ミラーの描画設定
③コライダーの衝突設定
④光源が影響を与えるオブジェクトの設定
⑤PostProcessingの設定
⑥Udonの制御(レイキャストなど)
⑦パーティクルのプレビューの表示・非表示

これで全部だと思いますが、他に思い出したら追記しておきます。ではそれぞれについて解説していきます。

①カメラの描画設定

カメラに対して映る・映らないを設定することが出来ます。
やり方はカメラコンポーネントの上のあたりにあるCulling maskで映したいレイヤーにチェックを入れ、それ以外はチェックを外せばOKです。

ちなみにMain Cameraでこの項目をいじっても、VRChat内のプレイヤーの視界に映らなくなるわけではないので注意。

②ミラーの描画設定

ミラーに映る・映らないを設定することが出来ます。
やり方はVRC_Mirrorコンポーネントの下のあたりにあるReflect Layersで映したいレイヤーにチェックを入れ、それ以外はチェックを外せばOKです。
面倒な方は[Show only players]や[Show players/world]を押すとレイヤーのプリセットが利用できます。

③コライダーの衝突設定

各レイヤーどうしが衝突するか・しないかを設定することが出来ます。
左上のメニューの[Edit]→[Project Settings]→[Physics]のところで設定できます。

VRChatに固定されているものもあり、それをいじろうとすると怒られます。
3D向けの衝突設定はPhysicsの項目であり、Physics 2Dではないので間違わないように注意してください。

④光源が影響を与えるオブジェクトの設定

特定の光源からの光を受ける・受けないを設定することが出来ます。
Lightコンポーネントの下のほうにあるCulling maskで光を受けてほしいレイヤーにチェックを入れ、それ以外はチェックを外せばOKです。

ただ、影を出す・出さないはオブジェクト単位で行うこともできるので、軽量化したいときや、特定のオブジェクトにだけ色を付けたい時、見た目を整えたい時などに使う用途が良いのではないかなと思います。

⑤PostProcessingの設定

唯一、PostProcessingは影響を与える・与えないではなく、PostProcessing Volumeのコンポーネントがついたオブジェクトを指し示すために使われます。

既存のレイヤーと分けたほうがパフォーマンスが良いという記述があるので、自分はPostProcessingというレイヤーを作って運用しています。
(補足:おそらくは、PostProcessing Volumeのコンポーネントがついたオブジェクトのみがあるレイヤーのほうが、検索処理をあまりしなくていいので軽量という話だと思いますが詳細は不明)

⑥Udonの制御(レイキャストなど)

中級者~向けですが、レイキャストを飛ばすときに特定のレイヤーだけ検出するようにすることが出来ます。

レイキャストはコライダーとかを検出するために放つレーザービーム的なやつですね。VRChatでメニュー開いた時に出るレーザーもレイキャストです。あれは可視化されていますが、普通は透明です。

とはいえ、レイキャスト使ったことないので、すみませんが書けることは少ないです。別件でCannyを漁っていたらレイヤー指定のレイキャストが動かないよみたいな内容らしきCannyが2つほどあがってたので、どうなのでしょう……。

⑦パーティクルのプレビューの表示・非表示

Hierarchyでパーティクルを選択したときに、Sceneビューの右下に出てくる小さいウィンドウの中に、Simulate Layersという項目があります。

デフォルトは選択したパーティクルをプレビューしますが、Simulate Layers を Nothing 以外にすると選んだレイヤーに所属したパーティクルが常時プレビューされます。Everythingにすると全てのパーティクルが常時プレビューされます。

レイヤーの設定の仕方

最初のほうに述べた通り、レイヤーは各ゲームオブジェクトに対して割り当てます。

したがって、hierarchyでゲームオブジェクトを選択した時に、Inspectorの右上辺りにレイヤーを切り替えられる場所が出てきます。ここでゲームオブジェクトにレイヤーを割り当ててあげることが出来ます。

一方でレイヤー自体については、0番目から31番目までレイヤーがあるのですが、そのいくつかは定義されておらず、未使用なものが存在します。なので、それらについては新しく好きな名前でレイヤーを定義して、追加してあげることが出来ます。

画面右上にLayerというボタンがあるので、それを押下するとレイヤー一覧が出ると同時にドロップダウンの一番下に[Edit Layer]という項目が出ます。

押下すると以下のようにレイヤーを追加できる画面が出てきます。空欄になっているところ(22~31番)は未定義なので、自由に名前を付けて追加することが出来ます。

既存のLayerについての説明

VCC(VRChat Creator Companion)で立ち上げたプロジェクトでは、既にVRChat用にいくつかのレイヤーがセットアップされています。

これらのセットアップされたレイヤーはVRChat内で幾つかの効果を持っていますので、それについてまずは解説していきたいと思います。

Layer0~7

こちらはUnityデフォルトのレイヤーとなっています。Unityデフォルトのレイヤーは書き換え等が出来なくなっています(名前もBuiltin Layerとなっていて、組み込みのレイヤーであることが伺えます)

Default
Unityのゲームオブジェクトでデフォルトで使われているレイヤーです。VRChatのアバターペデスタルはこのレイヤーのようです。

TransparentFX
Unityにはレンズフレアのシステムがありますが、その遮蔽物にならないレイヤーとしてこのレイヤーがデフォルトで指定されています。
(コンポーネント側のレイヤーの指定を変えれば、このレイヤー以外でも遮蔽物にならないように変更できそうです)

Ignore Raycast
レイヤーマスクの指定がない場合、Unityのレイキャストによって無視されます。一方でVRChatのレイキャストでは無視されないので、メニュー開いた時のレーザーなどはこのレイヤーに所属しているコライダーにブロックされる可能性があります。
(2023/10/23 追記 VRChatのカメラに映らないレイヤーのようです。また操作も阻害されないため、UIやUI Menuレイヤーよりも優秀な気がします……がVRChatのマニュアルに載っていないので、使えなくなる可能性はあると思います)

Water
昔のUnityの標準アセット(おそらくStandard Assetのこと?)の名残でWaterという名前になっています。WaterレイヤーはVRChatのミラーで映らないようになっているので注意が必要です(詳しくは記事後半で解説します)
また、Postprocessのレイヤーの標準レイヤーとしても使用されています。

UI
VRC内のメニューなど色々なUI系がここに入っていますので、このレイヤーを使用するのは避けたほうが良いでしょう。VRChat公式のマニュアルでは使用が非推奨になっています。
ここに所属しているオブジェクトはVRCのメニューを開いている時だけ表示され、インタラクトについてもメニューを開いた状態でレーザービームを当てた状態でのみインタラクトできます。
また他の既存のどのレイヤーとも衝突しない初期設定になっています。
そして、VRカメラに映らないレイヤーです。

使用が非推奨とされていますが、アップデートで壊れる可能性を踏まえていれば、個人的には使用してもいいかなと思っています。『メニューを開いたときだけインタラクトできる』や『VRカメラに映らない』は普通に魅力的なので……。

Layer8-21

こちらはVRChat側でセットアップされていて、実際に既に利用されているレイヤーです。
大体のレイヤーが特殊な役割を持っているので、利用する際は注意して使わないといけませんが、既に衝突判定とかが設定されている便利なものもあります。
VRChatのアップデートでレイヤーに所属するものが変わるケースがあるので、このレイヤーを使えば安心……というわけでもないです。

今回はVRChat側で用意したレイヤーの中で特殊なものだけ説明しますが、これに関してはアップデートで変化していることもあるので、間違っていたらご指摘お願いします。
(↑VRChatのマニュアルが整備されたのでそちらを確認すれば良さそうです)

Player
自分以外のプレイヤーのアバターが所属しています。

PlayerLocal
自分の頭を除いた、アバター部分が所属しています。頭は自分自身には見えないので省かれています。ミラーやカメラへの自分自身の描画はPlayerLocalの代わりにMirrorReflectionを使用することで、頭を含めた全身を描画してくれます。

UiMenu
レイヤー5のUIと性質は一緒です。プレイヤーのネームプレートはこのレイヤーに所属しています。

Pickup
持てるオブジェクト用のレイヤーです。PlayerとPlayerLocalに衝突しない設定になっています。

PickupNoEnviroment
Pickupとのみ衝突する設定となっています。

Walkthrough
プレイヤーはこのレイヤーと衝突しないような設定になっています。コライダー付きの机とか椅子とかをこのレイヤーにすると、プレイヤーにぶつからず、Pickupはぶつかる設定になってるので便利です。

MirrorReflection
かなり特殊なレイヤーです。
ミラーやカメラで自分のアバターを描画するためのレイヤーです。PlayerLocalとの違いは、こちらでは頭も描画されます。PlayerLocalも同時に描画対象にすると自分自身の首から下を2回描画することになり、Z-Fightingとかが起こります。VRCのミラーについては運営側が対策してくれてるみたいですが、自前で用意したカメラとかにアバターを映して、RenderTexture等に映像を流すときには注意が必要ですね。

このレイヤーはピックアップできず、衝突もなく、レイキャストの対象外なので、プレイヤーやオブジェクトのコライダーへの進入を判定するのにちょうどいいレイヤーです。

reserved2~4
基本的に使用は非推奨。このレイヤーにオブジェクトを所属させると、アップロード時に強制的にレイヤー0(Default)に移動させられてしまうようです。

上記以外のレイヤー
使われていないようなので、自由に使ってもよさそうです。ただし、衝突設定やCulling maskなどは要確認。

Layer22-31

自由に使えるレイヤーです。新しく作ることが出来ます。
注意点としては、新しく作った場合は衝突設定やCulling maskは全てチェックが入っている状態なので、必要に応じて設定を一通り見直したほうが良いと思います。

ですが裏を返せば、好きなように設定しても不都合は出ません。
例えばPlayerとPlayer Localには衝突しなくて、Pickupには衝突するレイヤーを作って、そこにコライダーのついた壁のオブジェクトを所属させると、ピックアップは持っていけないプレイヤーのみがすり抜ける壁が完成します。
上手く使いこなしましょう。

良く使われるテクニック

アバターにのみリアルタイムライトを当てる

レイヤーを駆使すると特定のオブジェクトにだけ、ライトを当てることができるので、レイヤーをPlayerとPlayerLocalとMirrorReflectionに設定したリアルタイムのライトで、アバターのみに光を当てるテクニックはよく使われます。

これの意味としては、
①アバターの明るさ・陰影のある程度の統一
②アバター以外のオブジェクトの光の計算を省くことによる、リアルタイムライトの軽量化
などがあります。

アバター向けのシェーダーはかなり多様化していて、様々なシェーダーがVRChat内で使われていますが、リアルタイムの光源のある・ないでそれぞれ違う処理をします。

リアルタイムライトが無いとShaderからは基本的に光源が有るかどうか分からないので、アバターは暗くなります。

Light Probeを置けば、ベイクの明るさを取得することが出来ますが、光がやってくる方向については基本的に分からないので、Shader側が決め打ちで適当に陰影を付けたり付けなかったりします。

なので、影なし&光の強さ(Intensity)が0.01でも大丈夫なのでリアルタイムのDirectional Lightをアバターにだけ当てるように用意してあげると、ここら辺が保証されます。

私は基本的にワールドには必ずおいています。
とはいえ、これをやっても異なるシェーダー間の差異はどうしても存在しますし、ユーザ側のパラメータ調整によってはいくらでも差異は生まれるので、そこは目をつぶっています。

影付きのリアルタイムライトを必要とするシェーダーへの保証

一部のシェーダーは影付きのリアルタイムライトを必要とします。
影付きのリアルタイムライトがないとDepth(深度情報)が取れないためです。

ですが、影付きのリアルタイムライトは比較的重いです。

そこで、何もオブジェクトが無い一つのレイヤーのみを照らす影付きのリアルタイムライトを用意します。何もオブジェクトが無くても何故かDepthが取れるので、最低限の負荷でDepthを必要とする一部のシェーダーの動作を保証できます。

VRカメラに映らないスイッチやピックアップを作る

(2023/10/23 追記 Ignore Raycastレイヤーに突っ込んでおくと、カメラに映らず、操作も可能みたいです。ピックアップとuGUIとインタラクト、全て問題なく操作できました。以下はIgnore Raycastでうまくいかなかったら試してみてください。)

これはよく使われるテクニックかは分かりませんが、何となく思いついて試してみたらいけたので書いておきます。

UI・UIMenuレイヤーを使うとVRカメラに映りません。
その性質を利用します。

しかし、スイッチやピックアップを丸ごとUI・UIMenuレイヤーに所属させてしまうと、メニューを開いたときしか操作できなくなり不便です。

そこで、インタラクトやピックアップに関するコンポーネントが付いたゲームオブジェクトはUI・UIメニュー以外のレイヤーを使います。
そして、外見(つまりメッシュレンダラー系)のゲームオブジェクトだけUI・UIMenuに所属させることで、操作しやすいカメラに映らないオブジェクトができます。

例えばピックアップでいけば以下のような感じです。スイッチは動かす必要がなければ親子関係にする必要もないでしょう。

ただ、UI・UIMenuはVRChatとしては非推奨みたいなので、アップデートの際におかしくなる可能性はあるかもしれません。

レイヤーに関するあれこれの検証

レイヤーは公式ドキュメントがないせいか、真偽の怪しい情報が流れていることがあります。今回の記事では2つほど検証してみました。

①Waterレイヤーはミラーに映らない仕様

これは前に情報を見てから、「本当だろうか?」とずっと疑問に思っていました。なのでこれを機に検証しました。

Defaultレイヤーのオブジェクト(左)とWaterレイヤーのオブジェクト(右)を用意して撮影したところ、以下のようになりました。

分かりにくい写真ですみません、奥がミラーで手前が実物です
右側のオブジェクトがミラーに映っていません

なんと映っていません!
ちなみにレイヤーを切り替えていったところPlayerLocalレイヤーも映らないようでした。

ミラーの設定は以下の通り、Reflect LayersをEverythingにしていますので、全て映るはずなのですがWaterレイヤーとPlayerLocalレイヤーは映りませんでした。

原因究明のために検索を掛けていたところ、公式のVRC_MirrorReflectionのページにこのような記述がありました。

Objects on the Water layer are never rendered in mirrors.
(訳:Waterレイヤーのオブジェクトはミラー内で描画されないよ)

……はい!ということでなんと仕様だったみたいです。

PlayerLocalはなんなんだ、という話になりますが多分書いていないですが、親切心でしょうね。
Mirror Reflectionレイヤーと同時にチェック入れると、自分のアバターの胴体が2重描画されて、盛大にZ-fightingのちらつきを起こすので。
(本当か?って思う人は自前で用意したカメラ+Render Textureでやると分かると思います)

知り合いから前に聞いた話なので話半分で聞いてほしいのですが、VRCミラーの中ってもう一回描画してるらしいです。なので実は鏡というよりも、鏡用の板の中にもう一個ワールドを作っているような感じみたいです。

憶測ではありますが、その描画するフェイズでWatarレイヤーとPlayerLocalレイヤーの描画を除外しているという仕組みなのかなと考えています。

正直自分は普通に映ると思ってたので予想外でした。
映らないって情報も、Waterって名前に釣られて水シェーダーを置いたからかと思ってたので、余計にびっくりでしたね。
でもこれはこれで、スイッチとかUI系のオブジェクトをWaterレイヤーに置いておけば、ミラーには映らないので便利かもしれませんね。

(補足:「水シェーダーがミラーに映らない」について
水シェーダーの大半はDepth(深度)情報を必要としますが、VRCのミラーの中ではDepthが正しく取れません。結果として描画に必要な情報が足りず、水シェーダーが描画されない or 変な風に描画されるということが起こります。

一部の水シェーダーはVRChat向けに特殊なやり方をしていたり、Depthをそもそも使ってないのでこの問題を回避しているケースがあります。
一方でUnity Asset Store製の水シェーダーは当然ながら無対策なので、基本的にミラーに映らないケースが大半です。これについてはそのうち別途記事にしようかなと思っています)

追記:MirrorがWaterレイヤーに映らない理由についてオノッチさん(https://twitter.com/onotchi_)から情報を頂きました。ありがとうございます!以下にそのリプライを貼らせていただきます。

個人的には凄く納得のいく話で、なるほど~~!!!!ってなってました。

②BakeryでCulling maskは有効なのか?

たまに、「BakeryってCulling mask反映してくれないですよね?」という話を耳にすることがあるのですが、きちんと気にしたことがなかったので、検証してみました。

結果からお話ししますが、
Bakeryどころか、そもそも標準のライトベイクシステムもCulling maskの設定を考慮していない」という結果が出ました。

標準のライトベイクシステムは反映してくれるもの、という思い込みがあったので、これは正直かなり予想外な結果でした(思わず設定間違えたかと思って、丁寧に見直したのですが特に問題はありませんでした)

検証は光が当たらないレイヤーに所属させたオブジェクト(左)と、当たるレイヤーに所属させたオブジェクト(右)を用意して、標準のライトベイクシステムとBakeryそれぞれでベイクすることで行いました。


結果としては、標準のベイクシステムとBakery両方とも光が当たっているものとして、ベイクを行っていて、レイヤーの設定は無視されています。

標準ライトベイク結果
Bakeryのライトベイク結果

つまりCulling maskの項目が関係あるのはRealtimeの光のみということですが、ではRealtimeの光とベイク結果が混在するMixedのライトでは面白い結果が出るのでは?と思い、ベイクしたところやはり違う結果が出てきました。

まずはMixed ライティングのオプションをデフォルトのShadowMaskモードのままベイクしてみました。当然ながらBakeryはLayerのことを何も考慮してくれないので、標準のベイクシステムでの検証です。

なるほど、このモードでは静的オブジェクトに対するライティングはRealtimeなのでLayerが考慮され、片方のオブジェクトは光が当たっていない状態になりますが、影はベイクシステムによるものなのでしっかり出るというわけですね!
では、MixedライティングのオプションをShadowMaskから、直接のオクルージョン(影)をベイクしないBake Indirectに変更すると……?

やはり!影も描画されなくなりました。
こちらも静的オブジェクトに対するライティングはShadowMaskと同条件ですが、こちらは影をベイクしませんので、光が当たっていないほうのオブジェクトの影は落ちなくなりました。

では、最後にMixedライティングのオプションをSubtractiveにして、ベイクしてみましょう。

このモードでは、静的オブジェクトに対するライティングがRealtimeの光ではなく、ベイクによって与えられるので、Layerを無視して光が当たっています。

以上の結果は「ベイクシステムがLayerを無視する」という考察を補強してくれていると思います。

正直な感想としてはぶっちゃけ面倒ですね……レイヤーであれこれしているうちに整合性が合わなくなってくる部分が発生しそうなので、細かいライティングの調整をレイヤーでやるのは避けたほうが幸せな気がします。
あくまでリアルタイムライトに絞った運用をするのが良さそうです。

一応ライトベイクにおいても、標準のライトベイクシステムにおけるSystemTag・Baked Tagや、Bakeryのbitmaskを利用することでレイヤーのような運用は出来ますが、そこそこ準備に手間がかかるのでうーん……って感じですね。
(Bakeryのbitmaskについては落雷さんの記事を参照するのがおすすめです)

さいごに

というわけで今回はレイヤーの話だったのですが、いかがだったでしょうか?

個人的にはかなり予想外の仕様を知れたりと、結構ためになりつつ、楽しく記事を書くことが出来ました。

検証してみると意外と思い込みが多かったので、もしこれ違うんじゃない?っていうのあったら教えてください。

ワールド制作初心者にとっても覚えることが多く、大変だと思うので、この記事を参考にちょこっとずつ覚えていただければ幸いです。

長文の読了、お疲れ様でした。


おまけ

検証用に既存の全てのレイヤーのオブジェクトを用意したワールドを作成しました。コライダー、カメラ不可視テスト、ピックアップ、インタラクト、uGUI、全部検証できるようにしておきました


この記事が気に入ったらサポートをしてみませんか?