がとーしょこらの技術録(旧)

記録や技術的な記事を書いていきます

VRChatで「見えない」を実装する

がとーしょこらです。
この記事はVRChat Advent Calender 2019の9日目の記事です。
昨日の記事は奈良阪さんによる『今よりもっと「クロスプラットフォーム」なVRChatを作りませんか?
』でした。
adventar.org

本記事ではVRChatで「見えない」を実装するいくつかの方法を紹介します。

VRChatでは見えないオブジェクトを実装することができます。
これは自分にだけ見えなかったり, 特定の方法でだけ見えなかったりすることができます。
これらをうまく使うといろいろなことができるようになります。

この記事で紹介したシェーダーを使った方法を実装した簡単なシェーダーは以下に置いておきます。
drive.google.com

見えないを実装する

いろいろな見えないを実装していきますが, そのすべてを以下の方法で実装できます。

  • 特別なシェーダーを使う
  • レイヤーを使う

しかし, 方法によってはアバターだけワールドだけでしか使えないものもあります。
(対象の方法には[アバター専用], [ワールド専用]と記載してます。)

ワールド専用はワールドのギミックとして見えないを実装するときに使います。
自分が作成したワールド以外でも見えないを実装したい場合はワールド専用以外の方法を用いりましょう。

特別なシェーダーを使う

Unityにおいて見えの部分の大半はシェーダーでなんとかできます。
見えないを実装する場合はそのカメラの状態を調べることでそのカメラに写すかどうかの判断をします。
アバターでは後述のレイヤーを変える方法は使えないのでこちらの方法を使うことになると思います。

レイヤーを変える

カメラに写るかどうかはそのオブジェクトが属するレイヤーによって変えることもできます。
しかし, レイヤーを自由に設定できるのはワールドだけなのでワールドだけで使える方法です。

レイヤーについては以前こちらに詳しくまとめましたので参考にしてください
https://vrcworld.wiki.fc2.com/wiki/Layers

オブジェクトのレイヤーを設定する方法

Unityでのレイヤーはオブジェクトのグループ分けの一種です。
これによってUnityではあるレイヤーのものだけ写せるだったり, あるレイヤーのものだけ持てるなどが実現できます。

例えばCubeにレイヤーを設定する場合はCubeのInspectorでLayerから設定したいレイヤーにチェックをいれることでできます。
(下記の画像の場合はDefaultレイヤーに設定されています。)
f:id:gatosyocora:20191207153559p:plain

方法まとめ

本記事で紹介する方法を先にまとめておきます。

シェーダー(アバター レイヤー(ワールド) その他
VRCCamera 画面サイズ1280x720 & 1920x1080 PlayerLocal, UiMenu, UI
カメラ 設定したRenderTextureのサイズ 設定したレイヤー
unity_CameraProjection[2][0] != 0.f or unity_CameraProjection[2][1] != 0.f PlayerLocal, 設定したレイヤー
自分 自分のローカルカメラが動いているかどうか MirrorReflection Headボーン以下に入れる(アバター)

 

VRCCameraで見えないを実装する

VRモードで使用できるカメラ(通称VRCCamera)で見えないを実装する方法です。

綺麗なワールドを作ったときにスクリーンショットにスイッチなどが写らないようにしたいときなどに使えます。

特別なシェーダーを使う

 VRCCameraの解像度が1280x720なのでこれで判断します。

bool IsInVRCCamera() {
	return (_ScreenParams.x == 1280 && 
		_ScreenParams.y == 720);
}

しかし, この状態だと撮影したスクリーンショットには写ってしまいます。
スクリーンショットの解像度は1920x1080なのでこれで判断します。

bool IsInVRCCameraSS() {
	return (_ScreenParams.x == 1920 && 
		_ScreenParams.y == 1080);
}

レイヤーを変える[ワールド専用]

VRCCameraにはPlayerLocalとUiMenuとUIレイヤーのものが表示されません。
よってこれらのレイヤーになるように設定してあげると良いです。

しかし, PlayerLocalレイヤーは鏡に写らず, 多くの場合UiMenuレイヤーも鏡に写らないように設定することが多いため, VRCCameraには写らないけど, 鏡に写したい場合はUIレイヤーをオススメします。

ワールドのカメラで見えないを実装する

ワールドに置いてあるカメラに写らないオブジェクトを実装する方法です。

ホラーワールドなどの演出や特殊なカメラの使い方の際に写したくないがあるときなどに使えます。

特別なシェーダーを使う

Cameraコンポーネントに設定したRenderTextureのサイズがカメラの画面サイズになるのでこれと一致しているかで判断します。

Properties {
	~
	_CameraRT ("Camera RenderTexture", 2D) = "white" {}
}

~
float4 _CameraRT_TexelSize;

bool IsInCamera() {
	return (_ScreenParams.x == _CameraRT_TexelSize.z && 
		_ScreenParams.y == _CameraRT_TexelSize.w);
}

レイヤーを変える[ワールド専用]

カメラではあらかじめ写さないレイヤーを設定することができます。

CameraコンポーネントのCullingMaskのチェックを外すことでそのレイヤーのオブジェクトは写らないようにできます。そして, 写したくないオブジェクトをそのレイヤーに設定します。

(下記ではStereoLeftレイヤーで本方法を試しています。)
f:id:gatosyocora:20191208015622p:plain
f:id:gatosyocora:20191208015830p:plain

鏡で見えないを実装する

ワールドに置いてある鏡(VRC_MirrorReflectionコンポーネントをつけたもの)に写らないオブジェクトを実装する方法です。

特別なシェーダーを使う

VRChat公式Discordで鏡に写らないシェーダーのコードを公開してくださっていた方がいました。
これを使うことで鏡に写っているオブジェクトか判別できます。
 


bool IsInMirror() {
	return unity_CameraProjection[2][0] != 0.f ||
		unity_CameraProjection[2][1] != 0.f;
}

レイヤーを変える[ワールド専用]

 鏡には仕様によりPlayerLocalレイヤーが写らないようになっています。

また, 鏡についているVRC_MirrorReflectionコンポーネントのReflectLayersでチェックを外すことで写らないレイヤーを追加することができます。
ネームプレートを鏡に写さないためにUiMenuレイヤーも写らないように設定していることが多いです。

このReflectLayersのチェックが外れているレイヤーに設定することで鏡で見えないオブジェクトにすることができます。
f:id:gatosyocora:20191207152933p:plain

自分に見えないを実装する

自分からだけ見えないオブジェクトを実装する方法です。

アバターで首につける装飾が視界の妨げになっているときなどに使えます。

特別なシェーダーを使う[アバター専用]

アバターではカメラが自分の環境だけでしか動かないことを利用して自分の視点かどうか判断をします。
カメラに色のついたオブジェクトを常に写しておきます。
自分の環境ならその色がカメラに写り, 他の人の環境ならカメラが写らず表示されません。
そしてそのカメラにつけたRenderTextureの色を見ることで判断できます。

このような実装で配布されている方がいるのでこちらを利用させていただくのが良いです。
booth.pm

レイヤーを変える[ワールド専用]

これはワールド専用の方法です。

通常, アバターの視点ではMirrorReflectionレイヤーのオブジェクトは見えないようになっています。
これを利用してワールドにおいてあるオブジェクトの場合, MirrorReflectionレイヤーにすることで見えないオブジェクトをつくることができます。

しかし, これは自分の視点から見えなくなるだけでカメラや鏡には表示されます。

Headボーン以下に入れる[アバター専用]

これはアバター専用の方法です。

VRCharの仕様でアバターのメッシュは複製されて, 片方をMirrorReflectionレイヤー, もう一方をPlayerLocalレイヤーになっています。
前述のようにアバターの視点からはMirrorReflectionレイヤーは見えなくなっています。
さらに1人称視点で視界の妨げになる頭のメッシュはHeadボーンのScaleを0にすることで疑似的に消しているようです。

これを利用し, Headボーン以下にオブジェクトを入れることでそのオブジェクトは見えなくすることができます。しかし, そのオブジェクトにVRC_IKFollowerコンポーネントをつけている場合, Headボーンの子ではなくなるようで見えてしまうので注意です。

最後に

特定の環境だけで見えなくするというのはVRやARならではだと思うので
うまく活用すると面白いものがつくれると思うのでぜひ活用してください!