りょーのブログ

いわゆる備忘録ってやつ

【VRChat】Dynamic Penetration System(DPS)の導入方法など

booth.pm

主にVRChatで使用される『Dynamic Penetration System』(以下DPS)というUnity用アセットについて解説します。
※Unityを使ってアバターにアクセサリーを付ける等の簡単な改変ができることを前提知識としています。
※筆者もUnityについてあまり深く理解しているわけではないので、間違いなどありましたらご指摘ください。

[更新履歴]
2022/11/20 初稿
2022/11/21 文章表現の微修正・「導入1'」を「導入1」の直後に移動・「Q&A」を追加 2022/12/14 Q&Aに「口などにOrificeを付けると首の付け根あたりに吸い込まれる」の項を追加

↓思いのほか長くなっちゃったから必要なとこだけ読んでね!!

前置き: DPSとは何か

一言で説明すると

  • Dynamic: 動的な
  • Penetration: 貫通
  • System: システム

ということで、Dynamic Penetration Systemは「モノが貫通する様子を動的に描画するシステム」ということです。
とりあえずDPSを使うだけならこれぐらいの理解で十分なので、この後の話は読み飛ばしても構いません。

もう少し詳しく

販売ページにはこう書いてあります:

本システムは、VRChat上でアバターを超えたペネトレーションのアライメントを可能にします。
射程距離に入ると自動的に貫通メッシュをオリフィスマーカーに合わせ、貫通メッシュを滑らかに変形させて貫通をシミュレートします。

……何のことだかわかりませんね! もう少しこなれた日本語でリライトするならこんな感じでしょうか。

DPSは、VRChat上のアバター間でモノを貫通させるときに、その位置を自動的に調整させるシステムです。
具体的には、Orifice(穴)マーカーの範囲内にPenetrator(貫通させるモノ)が入ってきたとき、そのPenetratorのメッシュをなめらかに変形させることで、自然に貫通しているように見せることができます。

これでもまだ文章が硬いかと思うので、具体例を出して説明しましょう。

ワールドに食べ物――たとえばお団子――があったとします。
これをフレンドの口元に持っていって食べさせるマネ(いわゆる「あーん」)をする、というのはメタバースの住人であれば誰もがやっているかと思います*1

そういうときに問題になるのが、微妙な位置ズレ。
VR機器のトラッキング性能やネットの同期ズレなど、さまざまな原因で口元からズレてしまうことがあります。

そこでDPSの出番です。今の例であれば、Unityを使って以下のようにDPSを組み込みます。

  • アバターの口元にOrifice
  • お団子本体にPenetrator

すると、DPSによって次のような調整が自動的に行われます:

  • お団子が口元に近づいたとき、お団子が口元の方を向くように変形する。
    • →DPSの用語を使うなら、Orificeに合わせてPenetratorが変形する。
  • そのままお団子を口内に入れていくと、口に入った部分が非表示になる。
    • →つまり、お団子が口を越えてほっぺから出てきたりしなくなる。

これで安心してお団子を食べられますね! シャペルちゃんもニッコリ。

今回の例ではPenetratorはワールドに置いてあるお団子でしたが、Penetratorをアバター側に組み込むこともできます。
なので、アバター側にお団子を出せるようなギミックを仕込んでおいて、そのお団子にDPSを入れておけばいつでもどこでもお団子を食べられるってことですね!*2
……謎にお団子を推してしまいましたが、VRChat上にあるモノであれば基本なんでもDPSは仕込めます。

注意1: Dynamic Boneがなくても使える

かつてVRChatで揺れものを表現するために使われていたDynamic Boneというアセットに名前が似ていることから誤解されがちですが、DPSを使うのにDynamic Boneは必要ありません。

この画像を見てもらうと、お団子の見た目は曲がっているけど当たり判定の部分は曲がっていないことがわかると思います。
これは「メッシュ自体を変形させているのではなく、見た目だけを曲げている」ということを意味しています。
つまり、DPSは実体としてはシェーダーであるということです。特に物理演算とかしているわけじゃないので、揺れものがDynamic BoneだろうがPhysBonesだろうが関係ないってことですね*3

注意2: アバターがVery Poorになるのはほぼ避けられない

DPSはその機能を実現するために、UnityのLightというコンポーネントを使用しています。
具体的には、Penetrator1つにつきLight1つ、Orifice1つにつきLight2つが使われます。

ところで、VRChatのAvatar Performance判定には以下のような基準が含まれています。

  • アバター内にLightが1つでも含まれたらPoor確定
  • アバター内にLightが2つ以上含まれていたらVery Poor確定

docs.vrchat.com

つまり、アバターにOrificeを実装した時点でPerformanceはVery Poor確定ということに注意してください*4
軽量化に気を使っている人であれば、同じアバターにDPSアリとDPSナシの2パターンを用意し、DPSを使う必要がない状況ではDPSナシのアバターを使う等の配慮が必要になることでしょう。

導入1: Orifice(モノが入る場所)を作る

それでは実際に導入してみましょう。まずは簡単なOrificeの方から説明します。
ここでは例として、アバターの口にOrificeを付けてみようと思います。

BOOTHでDPSを購入してダウンロードすると、中に RalivDynamicPenetrationSystemVx_xx.unitypackage というようなファイルが入っていると思います。
このunitypackageを、普段お使いのアバターのプロジェクトにImportしてください。

すると、Assets > RalivDynamicPenetrationSystem以下に何やらいろんなPrefabが入っているかと思います。
今回使うのは、その中の Orifice_HoleOrifice_Ring のどちらかです。

左がOrifice_Hole・右がOrifice_Ring

どちらも"穴"を表現するという点では同じですが、穴を貫通したPenetratorの表示のされ方に違いがあります。

  • Orifice_Hole は、穴を貫通した後の部分が非表示になります。
  • Orifice_Ring は、穴を貫通した後の部分をそのまま表示します。

これらは用途によって使い分けましょう。

今回は口にOrificeを付けようとしていて、口を通過したモノは非表示になってくれた方が嬉しいので Orifice_Hole を選ぶことにします*5

Orifice_Hole をHierarchyにドラッグしてあげると、何やら白い筒みたいなものが出てきます。
この筒に書かれている十字マークが"穴"の中心にあたる部分になります。

この十字マークがいい感じの位置(今回なら口元)に来るように移動・回転させてから、アバターのArmatureの追随してほしい階層(今回はHead)に入れてあげます。
微調整は後でできるのでざっくりで大丈夫です。

Headの中に入れた Orifice_Hole を右クリックし、"Unpack Prefab"を選びます。 それから、 Orifice_Hole 中にある Delete me after placement というオブジェクトをDeleteしてあげれば装着完了です。

後はいつも通りVRChat SDKからBuild & Testして動作確認を行ってあげてください。
『DPS TEST ROOM』というワールドに行くと、団子やらキュウリやらのPenetratorがいっぱい置いてあって便利です。

vrchat.com

もしPenetratorが意図しないところを貫通してしまったら、 Orifice_Hole の位置を都度調整しましょう。白い筒が見えなくてもOrifice自体は存在しているので、普通にUnityのギズモで動かせば大丈夫です。
また、「位置は正しいんだけど入っていく角度がなんか変」というパターンもありますが、その場合は Orifice_Hole のRotationを調整することで対応できます。

[2022/12/14追記]
……と思ったんですが、Head以下にOrificeを置いている場合はもうちょっと複雑な対応が必要なようです!(情報提供いただきました)

動作確認・調整が一通り終わったらBuild & Publishして導入完了です。 お疲れ様でした!

導入1': 全身にOrificeを付けたい人は補助ツールを使おう

先ほどは口にOrificeを付ける方法について説明しましたが、人によっては「耳とか他の穴にも何か突っ込めたら面白いんじゃね?」と思うかもしれません。
その場合、先ほどの手順を付けたいOrificeの数だけ繰り返してもいいのですが、OrificeがいっぱいあるとLightが多くなってアバターが重くなりますし、Orifice同士が干渉して挙動が怪しくなることもあります。

そこで、Expression Menuから複数のOrificeを切り替えて使えるようにしたツールが配布されていたりします。 ありがたく使わせてもらいましょう!
このツールについてはunitypackageの中にREADMEファイルがあるので、そちらを見てもらえればわかると思います。

booth.pm

導入2: Penetrator(貫通させるモノ)を作る

続いて、Penetratorを作ってみましょう。
(実は、Assets > RalivDynamicPenetrationSystemの中に名前が Penetrator_ で始まるPrefabがいくつか用意されています。もしその中にあなたの望み通りのPenetratorがあるのなら、そのままアバターに装着してあげればOKです)

まずはPenetratorになるモノの3Dモデルが必要です。今回はこのみたらし団子をお借りします。

booth.pm

3Dモデルをプロジェクトにインポートし、Hierarchyにドラッグしてあげます。

Unity上部のメニューに Tools > Raliv > Create Penetrator という項目が追加されているので、これをクリックします。

するとこんなウィンドウが出てくるので、My Modelのところに先ほど用意した3Dモデルを入れます。

すると何やら怒られます。それぞれ翻訳すると:

  • PenetratorのモデルはPenetrator用のシェーダーが適用されたマテリアルを使う必要があります!
  • モデルのシェーダーを Raliv > Penetrator に変えてみてください。

ということで言われた通りにします。3Dモデルのマテリアルを選択し、ShaderをRaliv > Penetratorに変更します。

それから先ほど開いたCreate Penetratorのウィンドウに戻ると表示される文言が変わっています。翻訳すると:

白い点をPenetratorの"基点"となる部分に移動してください。この点より前にある部分が変形します。
青い点をPenetratorの先端部分に移動してください。

ということで言われた通りにします。
お団子の場合は串が曲がったら変だと思うので、白い点("基点")はその手前にあるお団子のあたりに置きました。
青い点は見たままお団子のさきっぽに置きました。
X軸Y軸だけではなくZ軸方向もちゃんと位置合わせするのをお忘れなく!(1敗)

位置調整が終わったら"Generate Custom Model!"ボタンを押しましょう。

すると、名前が Penetrator_ で始まるオブジェクトが生成されます。
試しにさっき口に配置したOrificeに突っ込んでみると、確かにその位置に合わせて動いているのが確認できます。

後はこの生成されたオブジェクトを手に持たせるなりお好きなように使ってあげてください。
先ほど紹介した『DPS TEST ROOM』にはOrificeもいっぱい置いてあるので、動作確認に使わせてもらいましょう。

動作確認・調整が一通り終わったらBuild & Publishしてひとまず導入完了です。 お疲れ様でした。

……

Orificeは基本的には配置するだけでOKなんですが、PenetratorはShaderの設定項目になんかいろいろあります。
初期設定だとStandardシェーダーっぽい描画になってしまうのですが、下の方にあるToon Shadingってところにチェックを入れるとトゥーン調になったりします。
その他の項目については…… えーと、意欲のある人はいろいろいじってみてください(丸投げ)。

Q&A

DPSを使っていてつまづくかもしれない点について思いつく範囲で書いていきます。(随時更新予定)

PenetratorがOrificeに反応してくれない

まず月並みですが、導入の手順にヌケモレがないかを確認してみてください。
案外「Delete me after placement を消すところで必要なオブジェクトまで消してしまっていた」といったミスがあったりします。

導入が正しくできていることを前提とするなら、複数のOrificeが干渉してしまっている可能性があります。

白いOrificeではなく紫のOrificeに反応してしまっている

Penetratorは一度に1つのOrificeにしか反応できません。
このあたりの仕様は筆者もまだ調べきれていないのですが、Orificeにはどうやら優先順位みたいなものがあるようで、Penetratorが別のOrificeに吸い込まれているせいで自分の意図通りに動かないことがあります。
こういった問題が起こりうるため、ご自身のアバターに複数のOrificeを付けるなら補助ツールを使うか、各Orificeの表示・非表示をExpression Menu等から切り替えられるギミックを付けておきましょう。

余談ですが、1つのOrificeは同時に2つのPenetratorに反応できます。(3つ以上になると変な挙動になる)

Penetratorが変な折れ曲がり方をする

Orificeが裏返しになっている可能性が高いです。

Orificeには表裏の概念があります。「ORIFICE」という文字が読める向きで書いてある方が表で、その方向からPenetratorが入ってくることが意図されています。Unity上でOrificeオブジェクトを180度回転させてあげてください。
また、先述した複数のOrificeの干渉によってこのような現象が起こることもあります。

口などにOrificeを付けると首の付け根あたりに吸い込まれる

(情報を提供していただき追記いたしました、ありがとうございます)

きゅうりが…… 突き抜けてるーー!!

ほ、本当だ…… 気づかなかった……

DPSを使うアバターや角度によっては矛盾なく貫通しているように見えちゃう場合があるのですが、口にOrificeを付けているはずなのになぜか喉? 首の付け根? に吸い込まれていってしまうことがあります。

これは、VRChat側で「Headボーン以下に置かれているモノは、自分視点(Local)だとScaleが0になったように表示される」といった処理が行われることに起因するようです。
Scaleが0…… つまり0倍の大きさで表示(実質非表示に)することで、自分の視界に前髪・メガネ・顔の内側などといった余計なモノが表示されないようにしているわけですね。

その仕様自体はありがたいのですが、問題はOrificeの配置もScaleが0になったかのように振る舞ってしまう、ということにあります。

口に取り付けた Orifice_HoleHead の子にあたるモノなので、Scaleが0の位置に、つまり Head そのものの位置に穴があるかのように動いてしまうのです*6
これを回避するためには、Orifice_HoleHead の子として配置せず、なおかつ Orifice_Hole の位置は Head に追随させるような仕組みが必要となります。

以下にその手順を記します。

①. Armature を右クリックしてCreate Emptyを選び、空の GameObject を作る(名前は適宜変えてもOK)。
②. GameObject を選択した状態でInspectorのAdd Componentを押し、Parent Constraintを追加する。

③. Parent ConstraintのActivateを押す。
④. Sourcesの右下にある+ボタンを押すと"None (Transform)"という欄が出てくるので、そこにHeadをドラッグ&ドロップする。

⑤. Head の下に配置してある Orifice_HoleGameObject の下の階層に持ってくる。

ざっと説明しちゃいましたが、これでちゃんと口の位置にOrificeが来るようになります!

ノーモア突き抜け! おめめキラーン!

口以外にも耳の穴とか、Headに追随するようなOrificeを作りたい場合は同じ手順を使ってください。

耳にきゅうり……? さすがに状況がわからなくなってきたシャペルちゃん。

結び

簡単な説明に留めるつもりが結構な文字数になってしまいました。
これでも紹介しきれてない機能がまだまだあります。(Orificeにモノが入ったときにBlendshapeでモデルを動かしたり、Poiyomi Proっていう高機能なトゥーンシェーダーを組み込んだり……)

いろいろと面白いアセットなのに何故か調べても情報が全然見つからなかったので、とりあえず私の知識の範囲で書けることをここに書きました。
何かヌケモレがあったり、あるいは単に「うまく動かない」という相談などありましたら、コメントいただければわかる範囲で対応いたします。

それでは失礼いたします。

*1:以下の写真撮影はロケの都合上1人でやってますが…… みなさんはフレンドの方々とお楽しみくださいね!

*2:DPSはPenetratorとOrificeがセットになってはじめて動作するので、お団子を食べさせ合う場合は相手のアバターにもDPSが仕込まれていることが前提となります。

*3:もちろんDPSとPhysBonesを一緒に使って「揺れるし貫通もするモノ」を作ることも可能です。

*4:別にDPSユーザーが不当に意地悪されているわけではありません。単に光源処理がそれ相応に重い処理だから制限されているというだけです。

*5:Orifice_Ring を使うケースとしては、手にOrificeを付けてお団子をグニグニ曲げたい場合などが考えられます。

*6:Scaleが0で表示されるのはあくまでLocal動作なので、他人視点では正しく口に収まっているように見えます。