Unity
VR
SteamVR
HTCVive
OpenVR

HTC Vive ローディング画面(グリッド線・地平線)を消す方法

開発環境

Unity2017.3
SteamVR

HTC Viveのグリッド線とは

HTC Viveのローディング画面では、HTC Viveのデフォルトの「空間+グリッド線」が見える。
Unityのシーンの遷移時にグリッド線が見える。

しかし、ゲームによってはグリッド線は見せたくない場合がある。
例えば、「シーン1 ⇢ 黒フェードイン ⇢ 黒 ⇢ 黒フェードアウト ⇢ シーン2」のようなときにフェードインとフェードアウトの期間にグリッド線を見せたくない。

HTC Viveのグリッド線を消す

グリッド線を消す方法を2つ紹介します。
SteamVRまたはOpenVRを使用するので、まずは2つの違いから説明します。

SteamVRとOpenVRの違い

私の理解不足のところもあるため、間違い等があるかもしれません。ご容赦ください。

以下の動画の冒頭でSteamVRとOpenVRについて説明されています。
https://www.youtube.com/watch?v=4Gs5k2Fti1U

SteamVR:
 ・Valveが開発しているSDK
 ・OpenVRをラッピングしたもの
 ・Steamユーザーやディベロッパーがインストールしたものに使用されている名前

OpenVR:
 ・Valveが開発しているSDK
 ・VRアプリケーションを制作することができる
  (例)オブジェクトのトランスフォームを変更したり、ヘッドセットにテクスチャを描画している
  (例)コントローラーやヘッドセットとインタラクティブなデータのやり取りをしている
 ・OpenVRに対応するアプリケーションは、OpenVRに対応するすべてのデバイス上で動く
  (例)ゲーム ⇢ OpenVR ⇢ ハードウェア(HTC ViveやOculusなど)を繋いでいる

1. SteamVRを使用する場合

以下の方法でグリッド線は消えます。
1. SteamVR_LoadLevel.csをコンポーネントとしてゲームオブジェクトに追加します。
2. ShowGrid をFalseにします。
3. ローディング画面の背景色を変更しい場合は、BackgroundColorを変更してください。

Front、Back、Left、Right、Top、Bottomに画像をセットしないでください。
理由は、これらの変数はローディング画面用のSkyboxのテクスチャとして使用されるので、Skyboxにテクスチャがある場合は、ShowGridの有無に関係なく、グリッド線が強制的に表示されるためです。

2. OpenVRを使用する場合

SteamVRを使用する方法では、ローディング画面のグリッド線が消えても、地平線のように見えるグラデーションは消えません。
SteamVRのスクリプトを確認しましたが、グリッド線とグラデーションを同時に消すための処理はないようです。
なので、今回はOpenVRを直接使用して、これらを消します。

スクリプトの概要

カメラ(プレイヤー)を6枚のテクスチャで覆ってしまうことで、カメラにはテクスチャしか見えないので、結果的にグリッド線や地平線が見えなくなります。
また、RenderOverlayを呼び出すと、StopRenderingOverlayゲームの進行やローディング画面に関係なく、ヘッドセットに直接テクスチャが描画されます。

OverlayRenderer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Valve.VR;

public class OverlayRenderer : MonoBehaviour
{
    /// <summary>
    /// オーバーレイ用のテクスチャ。
    /// OpenVRがテクスチャの非圧縮にのみ対応しているので、テクスチャは非圧縮に設定する。
    /// </summary>
    [SerializeField]
    public Texture OverlayTexture = null;

    private Vector3[] _ScreenPos = {
            new Vector3( 0f,  2f,  2f),   // 前
            new Vector3( 0f,  2f, -2f),  // 後ろ
            new Vector3( 2f,  2f,  0f),   // 右
            new Vector3(-2f,  2f,  0f),   // 左
            new Vector3( 0f,  2f,  0f),   // 上
            new Vector3( 0f,  0f,  0f),   // 下
        };

    private Vector3[] _ScreenRot = new Vector3[] {
            new Vector3(  0f,   0f,  0f),      // 前
            new Vector3(  0f, 180f,  0f),      // 後ろ
            new Vector3(  0f,  90f,  0f),      // 右
            new Vector3(  0f, -90f,  0f),      // 左
            new Vector3(-90f,   0f,  0f),      // 上
            new Vector3( 90f,   0f,  0f),      // 下
        };

    /// <summary>テクスチャのオーバーレイを描画する。</summary>
    public void RenderOverlay()
    {
        if (OverlayTexture == null)
        {
            return;
        }

        var overlay = OpenVR.Overlay;
        if (overlay == null)
        {
            return;
        }

        for (int i = 0; i < 6; i++)
        {
            // オーバーレイを新規作成する。
            var OverlayHandle = _GetOverlayHandle("Overlay_" + i.ToString(),
                                                    transform,
                                                    _ScreenPos[i],
                                                    _ScreenRot[i]);

            // オーバーレイにテクスチャを貼り付ける
            if (OverlayHandle != OpenVR.k_ulOverlayHandleInvalid)
            {
                var texture = new Texture_t();
                texture.handle = OverlayTexture.GetNativeTexturePtr();
                texture.eType = SteamVR.instance.textureType;
                texture.eColorSpace = EColorSpace.Auto;
                overlay.SetOverlayTexture(OverlayHandle, ref texture);
            }

            // オーバーレイを描画する。
            overlay.ShowOverlay(OverlayHandle);
        }
    }

    /// <summary>オーバーレイの描画をやめる。</summary>
    public void StopRenderingOverlay()
    {
        var overlay = OpenVR.Overlay;
        if (overlay == null)
        {
            return;
        }

        for (int i = 0; i < 6; i++)
        {
            // オーバーレイを新規作成する。
            var OverlayHandle = _GetOverlayHandle("Overlay_" + i.ToString(),
                transform,
                _ScreenPos[i],
                _ScreenRot[i]);

            // オーバーレイの描画をやめる
            overlay.HideOverlay(OverlayHandle);
        }
    }

    /// <summary>
    /// OpenVRのオーバーレイの新規作成または作成済みの再利用を行う。
    /// </summary>
    /// <returns>オーバーレイを返す。</returns>
    /// <param name="overlayName">オーバーレイの名前</param>
    /// <param name="transform"></param>
    /// <param name="widthInMeters">Width in meters.</param>
    private ulong _GetOverlayHandle(string overlayName, Transform transform, Vector3 pos, Vector3 rot)
    {
        // オーバーレイのハンドルの初期化
        ulong handle = OpenVR.k_ulOverlayHandleInvalid;

        // オーバーレイを取得
        var overlay = OpenVR.Overlay;
        if (overlay == null)
        {
            return handle;
        }

        // オーバーレイのキーを作成
        var key = SteamVR_Overlay.key + "." + overlayName;

        // オーバーレイのキーの検索
        // キーが見つかった場合、オーバーレイのハンドルにオーバーレイ(ID)がセットされる。
        // キーが見つからなかった場合、オーバーレイのハンドルにk_ulOverlayHandleInvalidがセットされる。
        var error = overlay.FindOverlay(key, ref handle);
        if (error == EVROverlayError.None)
        {
            return handle;
        }
        else
        {
            // キーがない場合、キーを新規登録する。
            // また、オーバーレイのハンドルにオーバーレイ(ID)がセットされる。
            error = overlay.CreateOverlay(key, overlayName, ref handle);
        }

        if (error == EVROverlayError.None)
        {
            // 上記の処理でエラーがない場合

            // オーバーレイのアルファを設定する。
            overlay.SetOverlayAlpha(handle, 1.0f);
            // オーバーレイの幅を設定する。
            overlay.SetOverlayWidthInMeters(handle, 5f);

            // D3D textures are upside-down in Unity to match OpenGL.
            // テクスチャタイプがDirectXの場合、Unityでは上下反転するらしいので、正しく表示するために修正する。
            if (SteamVR.instance.textureType == ETextureType.DirectX)
            {
                var textureBounds = new VRTextureBounds_t();
                textureBounds.uMin = 0;
                textureBounds.vMin = 1;
                textureBounds.uMax = 1;
                textureBounds.vMax = 0;
                overlay.SetOverlayTextureBounds(handle, ref textureBounds);
            }

            // Convert from world space to tracking space using the top-most camera.
            var vrcam = SteamVR_Render.Top();
            if (vrcam != null && vrcam.origin != null)
            {
                // オーバーレイの座標と回転を設定する。
                var offset = new SteamVR_Utils.RigidTransform(vrcam.origin, transform);
                offset.pos.x = pos.x;
                offset.pos.y = pos.y;
                offset.pos.z = pos.z;
                offset.rot = Quaternion.Euler(rot);

                // オーバーレイの座標と回転をHMD用に変換する。
                var t = offset.ToHmdMatrix34();

                // オーバーレイの座標と回転を絶対値に変換する。
                overlay.SetOverlayTransformAbsolute(handle, SteamVR_Render.instance.trackingSpace, ref t);
            }
        }

        return handle;
    }
}

参考サイト

SteamVRにおいて「待」を表示させずに次のシーンをロードする方法
HTC Vive の音声ミラーをスクリプト制御する

まとめ

HTC Viveのローディング画面のグリッド線を消す、または、隠すことができました!
グリッド線を隠す方法は力技なので、他の方法もあるかと思います。
また、OpenVRには他にもできることがたくさんあるので、SteamVRでできないと行き詰まったら、OpenVRを直接使用してみるのも良いでしょう。

理解が間違っているところや他の方法をご存知の場合は、コメントにて教えていただけると幸いです。