ForgeVision Engineer Blog

フォージビジョン エンジニア ブログ

UnityとHTC Viveで鏡を作る方法

はじめに

こんにちは!VR事業部の長谷川(id:waffle_maker)です。

Unity 5.4から、SteamVRでReflection(反射)とRefraction(屈折)が正しく表示されず、非常にハマりました。

どうやら、CameraのTarget EyeをBothにした場合に、シェーダー側がそれに対応していないとダメみたいです。

今回はUnityとSteamVRで水…ではなく、鏡を表示する方法について紹介します。

バージョン情報など

各種バージョン情報は以下の通りです。

HTC Viveの購入はこちら

実際に試してみる

  1. 新規プロジェクトを作成し、SteamVR PluginとThe Lab Rendererをインポートします。
  2. Main Cameraを削除し、[CameraRig]プレハブをシーンに配置します。
  3. [ここ](https://forum.unity3d.com/threads/reflection-rendering-wrong-in-openvr-htc-vive.398756/#post-2726226)からMirror.unitypackageをダウンロードし、インポートします。 thank you! dan-kroymann.
  4. Mirror.csでエラーが発生するので、GetProjectionMatrixメソッドの第3引数を削除します。
    Valve.VR.HmdMatrix44_t proj = SteamVR.instance.hmd.GetProjectionMatrix(eye, cam.nearClipPlane, cam.farClipPlane, SteamVR.instance.graphicsAPI);
    
    Valve.VR.HmdMatrix44_t proj = SteamVR.instance.hmd.GetProjectionMatrix(eye, cam.nearClipPlane, cam.farClipPlane);
    
  5. Mirrorプレハブをシーンに配置します。 やったー!できたー! f:id:waffle_maker:20170503042723p:plain ※VRで見ないと分からないですよね…

まとめ

自分自身のアバターを表示するVRコンテンツでは、鏡を表示することで自分自身がその世界に居ると認識させることが出来ると思います。

鏡は軽い処理ではありませんが、導入部分などで鏡を見せることで没入感を高めることが出来ると思います。

是非活用してみて下さい!

視線追跡型VRゴーグル 「FOVE0」を試してみたお話 [サンプルプロジェクト調査編]

こんにちは! VR事業部の長谷川(id:waffle_maker)です。 前回はFOVE0の開封と準備でしたが、今回はUnityを使って色々と動かしてみます。

まずはキャリブレーションを実行

FOVEとPCを接続してランタイムを起動します。FOVEとPCは、HDMIケーブル1本、USBケーブル2本で接続します。

次に、前回の記事で記載した通り、タスクトレイにあるFOVE VRアイコンを右クリックし、「設定ツールの起動」を選択します。

f:id:waffle_maker:20170503060557p:plain

FOVEが接続されると上の画像の赤枠で囲んだ部分は、「ランタイムの停止」になっています。 もし、「ランタイムの開始」になっている場合は、ランタイムがまだ起動していないので、この場所をクリックして起動させます。

この状態でFOVEをかぶると、キャリブレーションを実行する画面が出ます。 キャリブレーションでは、目の前に出てくる緑色の球がゆっくり移動するので、それに合わせて視線を動かします。 1分程度で終わり、「キャリブレーションに成功しました。」という文字が出ると完了です。

Unity向けファイルは3種類

2017/05/03時点で、FOVEのDeveloperページには、Unityに関するファイルが3種類あります。

f:id:waffle_maker:20170503061322p:plain

以下、個別に解説します。

[1] Unityで作られたアプリケーション「EXAMPLE_CONTENT」を確認

まずは、EXAMPLE CONTENT というリンクをクリックして、サンプルアプリケーションを試してみます。 FOVEを接続してからexeを実行すると、このような画面が出ます。

f:id:waffle_maker:20170412194954j:plain

これは、部屋の中で視線の動きだけでライトを点灯、消灯させるデモです。

画像の中心にある緑色とピンク色の球が、自分の左右の眼の動きに合わせて動きます。壁にある小さな水色の球がスイッチです。

視線を動かし、緑かピンクの球を壁のスイッチに当てると、ライトが点灯します。もう一度当てると消灯します。

部屋の中の移動はできませんが、ポジショントラッキングカメラがあるので、そばに置いてあるテーブルに近づくことは可能です。

なお、ポジショントラッキングカメラは、OSVR1.3に付属していたカメラと同一ハードウェアでした。

[2] Unityのサンプルプロジェクト「UNITY_EXAMPLE」を確認

Unity5.5以上という指定があるので、先にver 5.5以降のUnityをインストールしておきます。

サンプルプロジェクトをUnityで開くと、このようなWarningが出ます。

f:id:waffle_maker:20170503055955p:plain

ここで、ContinueをクリックするとUnityが開きます。 ただし、下記のWarningが出ています。

f:id:waffle_maker:20170503061415p:plain

これらの原因は不明ですが、FOVEを動作させることができたので詳細は未調査です。

では、サンプルシーンを解説します。 SampleSceneというシーンを開くと、このような画面になります。

f:id:waffle_maker:20170503060224p:plain

Hierarchy ViewにあるSceneと書かれているオブジェクトは、部屋のモデルを集めたものです。

Fove Rigの子オブジェクトである、Fove Interfaceオブジェクトおよびコンポーネントが処理の中心です。

Fove Interfaceコンポーネントは、FOVEで視線入力を検出すると、FoveInterface.IsLookingAtColliderというメソッドで検出したことを通知します。 例えば、視線で見つめたものを変化させたいときは、変化させたいオブジェクトでFoveInterface.IsLookingAtColliderを呼ぶことで、視線入力を知ることができます。

今回のデモでは、部屋にあるCubeオブジェクトにアタッチされているFove Look Sampleスクリプトが、FoveInterface.IsLookingAtColliderを呼んでいます。

f:id:waffle_maker:20170503060433p:plain

視線で見つめるとCubeが光る処理は下記の通りです。

/* FOVELookSample .csの一部*/
    // Update is called once per frame
    void Update () {
        if (FoveInterface.IsLookingAtCollider(my_collider))
        {
            if (light_enabled)
            {
                l.enabled = true;
                m.SetColor("_EmissionColor", l.color);
                DynamicGI.SetEmissive(GetComponent<Renderer>(), l.color);
                m.EnableKeyword("_EMISSION");
            }
            //bool check = FoveInterface.IsLookingAtCollider(my_collider);
        } else
        {
            gameObject.GetComponent<Renderer> ().material.color = Color.white;
            //GetComponent<Renderer>().shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On;
            m.DisableKeyword("_EMISSION");
            if (light_enabled)
            {
                l.enabled = false;
                DynamicGI.SetEmissive(GetComponent<Renderer>(), Color.black);
            }
        }
    }

また、Fove Interfaceの子オブジェクトには、SphereLeftとSpeherRightというオブジェクトがあり、ここにアタッチされているスクリプトにより Sphereオブジェクトの位置を検出することができます。

/* FOVE3DCursorLeft .csの一部*/
    void Update() {
        FoveInterface.EyeRays rays = FoveInterface.GetEyeRays();

        // TODO: calculate the convergence point in FoveInterface

        // Just hack in to use the left eye for now...
        RaycastHit hit;
        Physics.Raycast(rays.left, out hit, Mathf.Infinity);
        if (hit.point != Vector3.zero) // Vector3 is non-nullable; comparing to null is always false
        {
            transform.position = hit.point;
        }
        else
        {
            transform.position = rays.left.GetPoint(3.0f);
        }
    }

なお、現状のサンプルプロジェクトを調べた限りでは、FoveInstanceクラスのAPIは視線入力の結果を取得するget系の関数のみであり、何らかの値を設定するset系の関数は準備されていないため、視線入力の微調整は難しそうという印象を持ちました。

参考までに、FoveInstanceの定義を開いて、APIの一覧を下記に書き出しました。 Fove SDKAPI一覧は、下記に掲載されています。

Fove Unity SDK: Packages

#region Assembly FoveUnityPlugin, Version=0.5.6269.32729, Culture=neutral, PublicKeyToken=null
// <in-memory assembly>
#endregion

using System;
using Fove;

namespace UnityEngine
{
    public class FoveInterface : MonoBehaviour
    {
        public FoveInterface();

        public static EFVR_Eye CheckEyesClosed();
        public static bool CheckSoftwareVersions(out string error);
        public static bool EnsureEyeTrackingCalibration();
        public static string GetClientVersion();
        public static EyeRays GetEyeRays();
        public static GazeConvergenceData GetGazeConvergence();
        public static Vector3 GetHMDPosition();
        public static Quaternion GetHMDRotation();
        public static SFVR_Pose GetLastPose();
        public static Camera GetLeftEyeCamera();
        public static Vector3 GetLeftEyeVector();
        public static Vector3 GetNormalizedViewportPosition(Vector3 pos, EFVR_Eye eye);
        public static Camera GetRightEyeCamera();
        public static Vector3 GetRightEyeVector();
        public static string GetRuntimeVersion();
        [Obsolete("Please use IsEyeTrackingCalibrated instead")]
        public static bool IsCalibrated();
        public static bool IsEyeTrackingCalibrated();
        public static bool IsEyeTrackingCalibrating();
        public static bool IsHardwareConnected();
        public static bool IsHardwareReady();
        [Obsolete("You should get a reference and use the non-static version of this function.")]
        public static bool IsLookingAtCollider(Collider col);
        public static void TareOrientation();
        public static void TarePosition();
        public void ConnectCompositor();
        public void DisconnectCompositor();

        public struct EyeRays
        {
            public Ray left;
            public Ray right;

            public EyeRays(Ray l, Ray r);
        }

        public static class Version
        {
            public const int MAJOR = 1;
            public const int MINOR = 1;
            public const int RELEASE = 1;
        }
    }
}

[3] UnityのSDK「UNITY_PACKAGE」を確認

最後に、unitypackage形式のファイルを確認します。 Unityで新規プロジェクトを作り、unitypackageをインポートすると、先ほどのサンプルプロジェクトでも登場したFoveUnityPluginフォルダのみがAssetsフォルダに展開されます。

unitypackageのみを展開した場合

f:id:waffle_maker:20170503062704p:plain

Unityのサンプルプロジェクトの場合

f:id:waffle_maker:20170503062713p:plain

ざっと見る限りでは、それぞれのFoveUnityPluginフォルダの中身は同じでした。

ただし、視線の動きに合わせてオブジェクトを動かすには、sphereなどの適当なオブジェクトを準備し、[2]のサンプルプロジェクトに入っていたFOVE3DCursorRight.csとFOVE3DCursorLeft.csをアタッチする必要がありますが、これは[2]のみに入っています。 そのため、視線の動きを表示させたいときは、この2つのスクリプトを使う必要があります。

なお、「UNITY_PACKAGE」をダウンロードすると、pdfのマニュアルも同梱されているので、詳細はそちらをご確認ください。

終わりに

今回、一通りの使い方を整理しながら、UnityサンプルプロジェクトだけでなくFOVEのHPで提供されているサンプルアプリも試しました。 コントローラを持たず、首を振らなくても操作ができる視線入力は、VRにおいて新しい入力手段になりそうで、とても可能性を感じました。

一方、弊社で試した限りでは、現状のFOVEは視線認識の精度が不安定なところがあり、開発者キットであるという印象を持ちました。 弊社ではFOVEに限らずVRの案件に広く対応できますので、ご興味あればお気軽にお問い合わせください。

UnityでHTC Viveのスタンバイイベントを受け取る

こんにちは!VR事業部の長谷川(id:waffle_maker)です。今回はUnityでHTC Viveのスタンバイイベントを受け取る方法について紹介します。

はじめに

スタンバイモードとは?

ヘッドセットをテーブルに置くなどして一定時間が経過すると、バックライトやヘッドフォンを切って消費電力を抑える状態になります。

ヘッドセットを見ると、バックライトが消えていることが確認できます。

youtu.be

SteamVRステータスモニタ上では、以下の様にスタンバイモードへの移行を確認できます。

f:id:waffle_maker:20170503025239p:plain f:id:waffle_maker:20170503025227p:plain

今回はこのスタンバイモードの開始と終了イベントを受け取ってみまます。

バージョン情報など

各種バージョン情報は以下の通りです。

HTC Viveの購入はこちら

実際に試してみる

  1. 新規プロジェクトを作成し、SteamVR Pluginをインポートします。

  2. Main Cameraを削除し、[CameraRig]プレハブをシーンに配置します。

  3. StanbyModeDetectorゲームオブジェクトを作成し、下記スクリプトをアタッチします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StanbyModeDetector : MonoBehaviour {

    // Use this for initialization
    void Start () {
        SteamVR_Events.System(Valve.VR.EVREventType.VREvent_EnterStandbyMode).Listen((Valve.VR.VREvent_t args) => {
            Debug.Log("スタンバイモード開始");
        });
        SteamVR_Events.System(Valve.VR.EVREventType.VREvent_LeaveStandbyMode).Listen((Valve.VR.VREvent_t args) => {
            Debug.Log("スタンバイモード終了");
        });
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}

実行してヘッドセットを机に置いて10秒ほどでスタンバイモードに移行して開始ログが出力されました! その後、ヘッドセットを動かしたら終了ログが出力されました!

f:id:waffle_maker:20170503032614p:plain

トラブルシューティング

  • [CameraRig]を使用しない場合は、イベントを検出できないようです。

SteamVR Pluginがちょっと古い場合

下記バージョンではイベントの取得方法が異なりました。

取得方法は以下の様な感じです。

SteamVR_Utils.Event.Listen("EnterStandbyMode", (object[] args) => {
    Debug.Log("スタンバイモード開始");
});
SteamVR_Utils.Event.Listen("LeaveStandbyMode", (object[] args) => {
    Debug.Log("スタンバイモード終了");
});

まとめ

このようにスタンバイモードの検出ができると、色々と活用できそうです。

例えば・・・

  • 普段はPVを再生しておき、ヘッドセットを被ったらコンテンツを開始する。
  • ヘッドセットを被ったら愛犬が走り寄って来る。

また、EVREventType列挙型には他にも多数のイベントが定義されていますので、調べて見るのも良いと思います。

面白い活用方法を思いついた方は是非こっそり教えて下さい!

*1:SteamVR Pluginの旧バージョンを入手したい場合は、githubからどうぞ。