e.blog

主にUnity/UE周りのことについてまとめていきます

HTC Viveの埋め込みカメラの映像をテクスチャとして取得・表示する

概要

HTC Viveのヘッドマウントディスプレイには、フロントカメラが埋め込まれています。
このカメラは、メニュー表示時にカメラを起動し外の状況を確認したり、ゲーム画面自体にオーバーレイで周りの状況を表示して、HMDを脱がなくても色々とできるように、という配慮がなされています。

カメラ自体は通常のWebカメラと違いはないので、Unityからカメラの映像をテクスチャとして取り込み、それを利用することができます。

WebCamTextureでも取得はできるようなのですが、SteamVRからもAPIが提供されているので、そちらを利用する方法をメモしておこうと思います。

なお、今回の記事はこちらのValveのコードを参考にしています(というかほぼコピー)。

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

public class ViveCam : MonoBehaviour
{
    [SerializeField]
    private bool _undistorted = true;

    [SerializeField]
    private bool _cropped = true;

    [SerializeField]
    private bool _followTrakking = false;

    [SerializeField]
    private Transform _target;

    [SerializeField]
    private Material _material;

    #region ### MonoBehaviour ###
    private void OnEnable()
    {
        EnableSteamVRCamera();
    }

    private void OnDisable()
    {
        DisableSteamVRCamera();
    }

    private void Update()
    {
        UpdateCameraTexture();
    }
    #endregion ### MonoBehaviour ###

    private void UpdateCameraTexture()
    {
        var source = SteamVR_TrackedCamera.Source(_undistorted);
        var texture = source.texture;

        if (texture == null)
        {
            return;
        }

        _material.mainTexture = texture;

        float aspect = (float)texture.width / texture.height;

        if (_cropped)
        {
            var bounds = source.frameBounds;
            _material.mainTextureOffset = new Vector2(bounds.uMin, bounds.vMin);

            float du = bounds.uMax - bounds.uMin;
            float dv = bounds.vMax - bounds.vMin;

            _material.mainTextureScale = new Vector2(du, dv);

            aspect *= Mathf.Abs(du / dv);
        }
        else
        {
            _material.mainTextureOffset = Vector2.zero;
            _material.mainTextureScale = new Vector2(1f, -1f);
        }

        _target.localScale = new Vector3(1f, 1f / aspect, 1);

        if (_followTrakking)
        {
            if (source.hasTracking)
            {
                var t = source.transform;
                _target.localPosition = t.pos;
                _target.localRotation = t.rot;
            }
        }
    }

    private void EnableSteamVRCamera()
    {
        var source = SteamVR_TrackedCamera.Source(_undistorted);
        source.Acquire();

        // カメラが認識されていなかったらdisableにする
        if (!source.hasCamera)
        {
            enabled = false;
        }
    }

    private void DisableSteamVRCamera()
    {
        _material.mainTexture = null;

        var source = SteamVR_TrackedCamera.Source(_undistorted);
        source.Release();
    }
}

ポイントは、SteamVR_TrackedCameraを使って該当カメラを有効化し、さらに有効な場合に、Updateメソッド内で、カメラの映像をTextureとして受け取ってそれを更新する、というものです。
外部カメラからの映像をなにかに使ったり、あるいはゲーム中の安全確保のために、一定距離近づいたら外部カメラ有効化してそれを表示する、みたいな使い方もできるかもしれません。

ということで、使い方を知りたかったのでメモとして書きとどめておきます。