以前の記事でReflection Probeを使って鏡を作成しました。
今回はレンダーテクスチャを使って鏡の機能を作成したいと思います。
レンダーテクスチャはカメラに映った映像をそのままテクスチャに出力し、そのテクスチャをマテリアルに設定してカメラのリアルタイムな映像をゲームオブジェクトに流すことが出来る機能です。
なので、鏡に見立てたゲームオブジェクトにカメラを配置し、その映像を鏡にレンダーテクスチャを使って映せば鏡の機能が出来そうです。
実際には少し問題が出ますが、とりあえず作ってみましょう。
鏡機能を作成する
それでは鏡機能を作成していきます。
鏡用のゲームオブジェクトを作成する
まずは鏡のゲームオブジェクトを作成します。
ヒエラルキー上で右クリックから3D Object→Quadを選択し、名前をMirrorとします。
Mirrorゲームオブジェクトを選択し、インスペクタのTransformの値を以下のように設定します。
Scaleを調整して鏡のサイズを変更し、Positionを調整して地面に立つような感じにします。
カメラゲームオブジェクトを配置
次に鏡に映す映像を取るカメラを作成します。
ヒエラルキーでMirrorゲームオブジェクトを選択した状態で右クリックからCameraを選択します。
CameraのTransformのRotationを調整し、鏡の前面にCameraのZ方向(青矢印)が向くようにします。
今回は以下のようにしました。
シーンビューを見ると以下のように鏡の前面をカメラが映すようにします。
レンダーテクスチャの作成とカメラへの設定
カメラに映った映像をレンダーテクスチャに出力する必要があるので、新しくレンダーテクスチャを作成します。
Assetsフォルダ内で右クリックし、Create→Render Textureを選択し、名前をMirrorRenderTextureとします。
鏡に映す映像の精度を上げるにはMirrorRenderTextureのインスペクタでSizeの値を増やします。
デフォルトでは256なので減らす時も増やす時も2で割るか掛けるかした数値を設定します。
例えば512です。
出来たMirrorRenderTextureを先ほど作ったカメラのOutput Textureにドラッグ&ドロップしてカメラ映像をMirrorRenderTextureに出力します。
Unityを実行するとMirrorRenderTextureにリアルタイムな映像が出力されていきます。
鏡用マテリアルの作成と設定
MirrorRenderTextureに映像が出力されるようになりましたが、この映像を鏡のゲームオブジェクトに表示する必要があります。
鏡ゲームオブジェクトに設定するマテリアルを作成します。
Assetsフォルダ内で右クリックからCreate→Materialを選択し、名前をMirrorとします。
MirrorマテリアルをヒエラルキーのMirrorゲームオブジェクトにドラッグ&ドロップするか、MirrorゲームオブジェクトのインスペクタでMesh RendererコンポーネントのMaterialsのElement0にMirrorマテリアルをドラッグ&ドロップして設定します。
次にAssetsフォルダにあるMirrorマテリアルのBaseにMirrorRenderTextureを設定します。
こうすることでMirrorRenderTextureにリアルタイムに出力されたテクスチャがマテリアルのBaseに設定され、Mirrorゲームオブジェクトにその映像が表示されるようになります。
床を設置して、キャラクターを配置し、鏡に映像が流れるかを確認してみてください。
鏡に映る映像が逆になる
鏡が出来たような気がしましたが、実際にはカメラに映った映像をそのまま鏡ゲームオブジェクトに表示している為、実際の鏡のようにはならずキャラクターが右に移動すると鏡では左側に移動しているように映ります。
つまり鏡では左右反転して映像を映さないといけません。
鏡ゲームオブジェクトのScaleを調整して反転する
鏡に映る映像を左右反転させる為にヒエラルキーのMirrorゲームオブジェクトを選択し、インスペクタのTransformのScaleのXの値に-を付けて反転させます。
ScaleのXの数値にマイナスを付けると映像が反転します。
シェーダーグラフを使って映像を反転する
鏡ゲームオブジェクトのTransformのScaleのXの値にマイナスを付けて反転させることは出来ましたが、Scaleを変更したくない場合や鏡に映る映像にエフェクトを加えたい時はシェーダーグラフを使って左右反転をさせることも出来ます。
シェーダーグラフの使い方については以下の記事を参照してください(URPやHDRP等のスクリプタブルレンダーパイプラインでないと使用出来ません)。
今回はUniversal Render Pipelineに設定したプロジェクトで作成しています。
映る映像を左右反転させる為にUVについて考えます。
テクスチャのUVは横軸にU、縦軸にVを取り、それぞれ0~1の値の位置を持ちます。
なので、左右反転させる為にはUの値を1から引けば左右の位置が逆になります。
例えば0の位置であれば一番左ですが、1から引いて1-0=1で一番右に移動し、
1の位置であれば1-1=0で一番左になります。
これをシェーダーグラフで作成していきます。
Assetsフォルダ内で右クリックからCreate→Shader→Universal Render Pipeline→Lit Shader Graphを選択し、名前をMirrorとします。
このMirrorをダブルクリックしシェーダーグラフのウインドウを開きます。
表示するテクスチャをマテリアルのインスペクタで設定出来るようにしたい為、Mirrorシェーダーグラフに_MainTextureという名前のテクスチャプロパティを作成します。
テクスチャを反転し表示する処理を以下のように作ります。
_MainTextureプロパティをグラフ上にドラッグ&ドロップし、Sample Texture 2DのTextureに接続します。
UVノードからSplitノードを使ってUとVの成分を取得し、RがUなのでOne Minusノードを使って1から引きます。
その計算した値と、Vの成分からVector2の値を作り、それをSample Texture 2DのUVに接続します。
これで左右反転が出来たので、Sample Texture 2DをFragmentのBase Colorに接続します。
One Minusノードを使わずに、Invert Colorsノードを使っても同じように出来ます。
Mirrorマテリアルのシェーダーを変更する
AssetsフォルダのMirrorマテリアルを選択し、インスペクタでShaderの部分を押し、Shader Graphs→Mirrorを選択します。
Mirrorマテリアルの_MainTextureにMirrorRenderTextureを設定します。
先ほどMirrorゲームオブジェクトのTransformのScaleのXに-2を設定して左右反転させましたが、今度はシェーダーグラフで反転させるようにしたので、ScaleのXは2に戻しておきます。
Unityを実行して動かすと以下のように映像が左右反転され鏡の機能が出来ました。
鏡機能の調整
鏡機能は出来ましたが、鏡に主人公を近づけると鏡には実際のキャラクターよりも大きく表示されています。
そこで少し調整してリアルな感じの表示にしたいと思います。
カメラの位置が鏡の表面と同じ位置にある為に実際よりも大きく表示されているので、カメラを鏡の後ろに移動させ鏡に映る映像を小さくします。
上のような位置にカメラを動かしました。
鏡ゲームオブジェクトが邪魔になる
カメラを鏡の後ろに移動させましたが、鏡の後ろ側を撮影するだけでキャラクターを写せなくなりました。
そこで、鏡用のカメラは鏡のゲームオブジェクトを映さないようにします。
Mirrorゲームオブジェクトを選択し、インスペクタのLayerでAdd Layerを押し、新しくMirrorレイヤーを作成し、Mirrorゲームオブジェクトに設定します。
次にMirrorの子のCameraを選択し、RenderingのCulling MaskでMirrorレイヤーのチェックを外します。
これでMirrorゲームオブジェクトの子のカメラではMirrorレイヤーを設定したMirrorゲームオブジェクトが写らなくなります。
Unityを実行し、鏡の子のカメラの位置をシーンビューで調整し、Transformの右の3つの丸を押して、CopyからPositionを選択してUnityの実行をやめた後にTransformの3つの丸を押しPaste→Positionを選択し貼り付けます。
今回調整して、以下のようになりました。
実行して試すと以下のような感じです。
鏡の後ろ側に移動してもキャラクターが写ってしまう
鏡に映るキャラクターが鏡の後ろ側に周っても鏡にキャラクターが写ってしまいます。
これはカメラが鏡の後ろ側にあり、映す範囲が鏡の後ろ側からある為です。
そこでMirrorの子のカメラの映す範囲を鏡のあたりからにします。
Mirrorの子のCameraのCameraコンポーネントのProjectionのClippingPlanesのNearを調整して鏡の前あたりから映るように変更します。
これでキャラクターが鏡の後ろ側に移動しても鏡に映らないようになりました。
鏡の後ろ側から表側に出てきてしまう
最後の問題としてMirrorゲームオブジェクトはQuadを使って作成しており、初期状態ではコライダがMesh Collider(メッシュの形状のコライダ)となっています。
コライダが衝突するのはコライダの表面だけで、QuadのMesh Colliderの場合は片方の面しか衝突しません。
そこでMesh Colliderコンポーネントを削除し、Box Colliderコンポーネントに変更して鏡の前面と後面両方で衝突するようにします(Box Colliderでは厚みがありボックスの外側が表面で衝突します)。
Box ColliderのSizeのZに少し幅を持たせます。
これはキャラクターを鏡の後ろから衝突させた時にキャラクターの一部が鏡の前面に見えてしまうのを避ける為です。
実際のBox Colliderの範囲は上のような感じです。
今回はQuadで作ったMirrorゲームオブジェクト自体にBox Colliderを取り付けましたが、Mirrorゲームオブジェクトの後ろ側に鏡を貼り付ける何らかのオブジェクトを用意し、鏡の後ろ側への侵入自体をさせないようにする方がいいかもしれません。
真実の鏡を作成する
鏡機能が出来たので最後に真実の鏡を作成してみます。
真実の鏡は本当の姿を映す鏡です。(´Д`)
ヒエラルキーのキャラクターをCtrl+Dキーを押して複製し、まったく同じアニメーションや動きをする別のキャラクターを作ります。
今回のキャラクターはスタンダードアセットのThirdPersonControllerのキャラクターを使います。
複製したThirdPersonControllerの名前をRealCharacterに変更します。
Assetsフォルダ内で右クリックからCreate→Materialを選択し、名前をRealとします。
Realマテリアルを選択しインスペクタでBase Map横の色の部分を押し赤色にし、Normal MapにEthan Normals、Occlusion MapにEthanOcclusionを設定します。
RealCharacterの子のEthanBodyとEthanGlassesにRealマテリアルを設定します。
お互いのキャラクターが衝突しないようにする
レイヤーに新しくPlayerとRealを作ります。
ヒエラルキーの元のキャラクターのThirdPersonControllerとその子であるEthanBody、EthanGlassesのインスペクタのLayerをPlayerに変更します。
RealCharacterとその子のEthanBody、EthanGlassesのインスペクタでLayerをRealに変更します。
ThirdPersonControllerとRealCharacterのTransformをまったく同じにします。
まったく同じ位置にいるのでお互いがぶつかり合ってしまいます。
そこでPlayerレイヤーとRealレイヤーのコライダがお互いに衝突しないようにします。
UnityメニューのEdit→Project SettingsでPhysicsタブを選択し、Layer Collision MatrixでPlayerとRealが交差する部分のチェックを外します。
これでお互いが衝突しなくなりました。
カメラに映るレイヤーを指定する
鏡に真実の姿であるRealCharacterを映すにはMirrorの子のCameraのRenderingのCullingMaskでPlayerのチェックを外せばいい事になります。
また、MainCameraのRenderingのCullingMaskでRealのレイヤーのチェックを外せばメインカメラにRealCharacterは映らなくなります。
やり方は先ほどMirrorの子のカメラでMirrorレイヤーを外した時と同じなので、同じように設定してください。
これでMainCameraにはThirdPersonController、鏡にはRealCharacterが写ることになります。
これで機能が出来ました。
上のようになります。
真実の鏡と言ってもカメラによって映す対象を変更しただけです。(^_^;)
今回の場合は同じ見た目ですが、見た目自体を変更しても出来ます(ただし移動や回転しても同じ位置や回転になるという前提です)。
終わりに
今回鏡のゲームオブジェクトはQuadから作っていますのでオブジェクトを形成する頂点が少ないです。
シェーダーグラフで頂点シェーダーを使って頂点に何らかの処理をさせたい場合はProBuilderや3DCGソフト等を使って頂点が多いオブジェクトを使う必要があります。
今回の場合は左右を反転させるフラグメント(ピクセル)シェーダーしか使っていないので特に問題はありません。