はじめに
自分が開発しているVRMViewMeisterでも使用している機能なのだが、Unityではカメラコンポーネントに深度とカリングマスクが備わっており、それによってカメラの映し出す映像の優先度をつけて重ねて表示できたりする。
(URPだとカメラはレンダータイプとスタック機能で同じことができる)
ではGodotで同じことをしたいと思ったときどうすればいいのか。調べてもドンピシャなテクニックが見つからなかった。
というわけで公式マニュアルを見て試した結果を今回紹介したい。
Unityでは
たとえばVRMViewMeisterではカメラを次のように構成して映像を表示している。
名前 | 深度 | カリングマスク |
---|---|---|
FrontMainCamera | 10 | Default,Player,Stage |
SubCamera | 50 | UserUI |
IKHandleCamera | 70 | Handle,SubPlayer |
Main Camera | 99 | TransparentFX,Ignore Raycast,...,UI |
※全てを同時に使っているわけではない。
VRMViewMeisterで表現したかったのは、下図のような3DモデルのIKを動かすのに、IKの操作ハンドル用の図形を常に全面で表示したかったのだ。
URP環境では
Main Cameraをレンダータイプ=基本にし、それ以外をオーバーレイにして次のように構成した。
URP環境でもこのカメラをすべて活用しているわけではない。
Unityでの実現方法
- Builtin RP
- 深度とカリングマスクを適切に使い分ける
- Universal RP
- レンダータイプとカリングマスク、スタックを使い分ける
至極簡単だ。(管理の仕方によってはすごく大変だろうが)
Godot では
じゃあGodotで同じことを実現したいと思ったときどうすればいいのか。
GodotにもCamera3Dというノードがある。同じことができれば一番スマートなのだろうが、Camera3Dでは似たプロパティは Cull Mask
しかなさそうだ。
それにCamera3Dを複数追加しても、そのままではそれぞれのカメラの映像を活用できない。
実現方法
結論から言うと、次の3つを使うことでだいたい同じことをできることがわかった。
- Camera3D
- SubViewport
- SubViewportContainer
公式マニュアルでは次のページだ。
サンプルの図形
ゲーム中に使う3Dノードは、普通にルートノードの任意の場所に追加していってよい。
シリンダー
VisualInstance3Dの Layers
を1にした。
球体
VisualInstance3Dの Layers
を2にした。
なおかつ、優先度別に重ねて表示したのがわかるように、あえてシリンダーの内部に埋もれて見えなくなるように配置した。
期待値
球体がシリンダーに埋もれて隠れることなく、表示されること
SubViewportContainerを追加する
映像を重ねたい Cull Mask
のまとまりの分、Camera3Dを用意すると思いきや、ただそのままルートノードに追加しても意味がない。
最初にするのは、 SubViewportContainer
をシーンに追加することだ。
SubViewportContainer
はControlノードの派生なので、こんな感じで配置する。
Control
->SubViewportContainer
Controlのプロパティは次のとおりにする。
Layout:
Anchors Preset: Rect全面
Transform:
Size:
x: [指定の値]
y: [指定の値]
Container Sizing:
Horizontal: 塗りつぶし
拡大: オン
Vertical: 塗りつぶし
拡大: オン
SubViewportContainerのプロパティは次の通りにする。
Layout:
Anchors Preset: Rect全面
Transform:
Size:
x: [指定の値]
y: [指定の値]
ポイントとなるのはこれらのプロパティだ。ControlとSubViewportContainer、それぞれのLayoutグループのTransformのSizeにて、目的とするビューポートのサイズを設定する。
そしてアンカープリセットは Rect全面
にすることによって、描画を全面に塗りつぶして表示させる。
そうしないとカメラ映像はUI周りに影響されて極端に小さかったりずれたりすることになる。
この段階ではこのような表示になる。
当然何も映らない。
必要な数のCamera3DとSubViewportを用意する
器が用意できたので実際に必要なCamera3DとSubViewportを SubViewportContainer
の子ノードとして追加していく。
ポイント
Unityの深度やスタックのように、目的の順番通りにSubViewportとCamera3Dのセットを追加してく必要がある。
つまり、 一番下に描画したいカメラ映像を1番目 に追加し、以後は順に追加していく。
1番目のSubViewportとCamera3D
SubViewportのプロパティは次の通り。
Size: ※Control・SubViewportContainerと同じ値
Viewport:
Transparent BG: オフ
Camera3Dのプロパティは次の通り。
Cull Mask: ※目的に応じてオン・オフする
Current: オン
この段階ではこのような表示になる。
見るべきポイントはプロパティ内のプレビュー表示だ。ちゃんとCamera3Dで設定した Cull Mask
の通りに3Dモデルや他のノードが表示されていること。
2番目以降のSubViewportとCamera3D
SubViewportのプロパティは次の通り。
Size: ※Control・SubViewportContainerと同じ値
Viewport:
Transparent BG: オン
Camera3Dのプロパティは次の通り。
Cull Mask: ※目的に応じてオン・オフする
Current: オン
この段階でこのような表示になる。
プロジェクトの設定
忘れてはならないのがプロジェクトの設定だ。
一般:
表示:
ウィンドウ:
ストレッチ:
モード: viewport
アスペクト: expand
設定は モード: disabled, アスペクト: keep
でもいいかもしれない。
設定が モード: viewport, アスペクト: ignore
だとこうなる。
設定が モード: viewport, アスペクト: keep
だとこうなる。
このあたりの表示方法は目的に応じて決めたいところだ。
注意点
カメラを動かす
無事映像が重なって表示されるようになったが、注意したいのはカメラが複数になったので、カメラを動かすときはすべて同時に同じ位置に動かさないといけない点だ。
例えばカメラを動かす別の操作用のノードを用意しておき、その子ノードとして RemoteTransform3D
を追加し、移動や回転を管理する。
あとはRemoteTransform3Dの親ノードを動かすようにすれば、すべてのカメラが同時に同じ動き方をするようになる。
特定の優先度のカメラだけ非表示にしたい
このやり方の場合、SubViewportのプロパティを変更すればよい。
Viewport:
Disable 3D: オン
これでそのSubViewportの3D映像が無効化される。つまり配下のCamera3DのCull Maskで指定された3Dモデルやノードは表示されなくなる。
まとめ
-
SubViewportContainerの子ノードとして、SubViewportを必要な数だけ追加する
-
SubViewportの子ノードとして、Camera3Dを追加する
-
SubViewportのプロパティは次のとおりにする
SubViewport | Transparent BG |
---|---|
1番目 | オフ |
2番目 | オン |
- カメラを動かす場合は全てのカメラが同じ動きになるように工夫する
終わりに
これでGodotでもUnityで深度別にオブジェクトの表示を分けていたように、優先度別に積み重ねて表示できる。
ただ、個人的にこれでいいのか疑問に思うのは3D映像がUIのノードの配下ベースになってしまう点だ。
3Dのままだったら、デフォルトのビューポートに直接3Dのカメラ映像が描画されていたが、このやり方だとあくまでUIのコントロールのコンテナの表示領域に沿って映した3D映像となる。
まわりくどい気がする。
もしGodotで実はこうすればいいんだよ、とか、こうしたほうがもっとスマートだよなどのご意見や発見があれば教えていただけると助かります。
このやり方でいいのであれば、カメラ映像を重ねることがGodotでもできるようになるので参考にしていただければ幸いだ。