HTC Vive Pro eye 眼动数据简单获取

文章目录

  • 前言
  • 一、unity软件的安装
  • 二、OpenXR与SteamVR插件的安装
  • 三、SRanipaRuntime SDK的安装与使用
    • 读取数据
  • 总结


前言

本文旨在介绍如何使用HTC Vive Pro eye,结合Unity,读取到眼动的数据。假设文章内容面向的是此前从未接触过unity以及刚上手HTC Vive Pro eye的同学(这也是我对自己的个人定位),文章会尽量写的详尽一些,如果阅读内容已经掌握,可以选择跳过。
另外,本文仅对一些不方便搜索得知的内容进行说明,如有疑问可以自行Google或者百度。
最终成果为一个简单的Demo,可以在unity的Log栏中看到输出的信息。如下图所示,右下角的信息栏中可看到读取到的眼动数据。
HTC Vive Pro eye 眼动数据简单获取_第1张图片
另外,本文支持转载,转载时请注明作者与出处。希望大家在研究的过程中可以收获到分享与交流的快乐。
如发现错误,请通过评论或是私信的方式批评指正。

感谢b站的大佬邓布利多军,在个人空间中分享过几例测试demo,本文也是在其耐心的指导下完成的。由衷的表示感谢。


一、unity软件的安装

unity的官方网址
选择红框中的“Get Started”

在进入的页面中,以学生身份或是个人身份,进行unity账户的注册(此处略过)。之后运行unity客户端时,需要个人身份验证才可以正常使用。
HTC Vive Pro eye 眼动数据简单获取_第2张图片
unity 下载界面
HTC Vive Pro eye 眼动数据简单获取_第3张图片
推荐使用unity hub,即unity官方提供的管理平台进行不同版本的客户端的安装。

选择想要下载的unity版本进行下载安装即可(至于安装过程中工具包的具体选择,视个人需要,笔者是在windows平台,所以选择了与windows相关的工具包)。

以下为几点个人在过程中遇到的问题:
1、通常在安装unity各版本的客户端时,由于网络问题,“Document”经常下载失败或是下载进度为零,可以直接选择不进行下载。
2、如要使用其他人的unity项目,请注意版本的问题。通常若两个项目的unity版本不一致,会发生错误。

新建项目时,选择3D项目即可。
HTC Vive Pro eye 眼动数据简单获取_第4张图片



二、OpenXR与SteamVR插件的安装

在新建的项目中,选择“window”,在子菜单栏中选择Package Manager
HTC Vive Pro eye 眼动数据简单获取_第5张图片
在Unity Registry中选择OpenVR Plugin,点击右下方的install即可。
HTC Vive Pro eye 眼动数据简单获取_第6张图片
有两种方式安装SteamVR。一是通过网页
unity store 搜索 steam vr 后的结果,选择红框对应的插件,选择在unity中打开即可。
HTC Vive Pro eye 眼动数据简单获取_第7张图片
HTC Vive Pro eye 眼动数据简单获取_第8张图片
第二种方法近似,即在菜单栏的windows中选择Assert Store,步骤与一近似。

下载好steamVR插件后, 需要在package manager中的My Asserts中选择进行import。
HTC Vive Pro eye 眼动数据简单获取_第9张图片
到这里,基本的工作就已经做好了。
可以在steam VR官方提供的场景中进行交互测试。
HTC Vive Pro eye 眼动数据简单获取_第10张图片
HTC Vive Pro eye 眼动数据简单获取_第11张图片

三、SRanipaRuntime SDK的安装与使用

HTC Vive 官方提供了SDK
安装网址
两个SDK都需要安装,根据步骤一步一步来即可。
HTC Vive Pro eye 眼动数据简单获取_第12张图片
安装好两个SDK后,可以在安装目录中,看到如此的两个文件夹。
在这里插入图片描述
在SDK文件夹下存放着说明文档,C、Unity、虚幻环境的demo。
HTC Vive Pro eye 眼动数据简单获取_第13张图片

此处提供一个方便查阅SDK 中 各API的网址,也是官方提供的,在下图所示路径下。
HTC Vive Pro eye 眼动数据简单获取_第14张图片
HTC Vive Pro eye 眼动数据简单获取_第15张图片
而在SRainipal文件夹中,则存放着本次实验的关键应用。具体使用方法请阅读SDK中提供的Eye_SRanipal_SDK_Guide。
HTC Vive Pro eye 眼动数据简单获取_第16张图片

在如图所示的路径下,可以找到HTC官方提供的demo package。在unity中选择该package file,进行import。
HTC Vive Pro eye 眼动数据简单获取_第17张图片

导入后,可以在Project中看到如图所示红框对应的文件夹ViceSR,选择Scenes中的Eye,双击EyeSample_v2。
HTC Vive Pro eye 眼动数据简单获取_第18张图片
之后则可以在Hierarchy中看到如此的结构。
HTC Vive Pro eye 眼动数据简单获取_第19张图片
笔者此处在Gaze Ray Sample_v2和Avatar_Fairy_V2组件的Inspector进行了勾选。unity通过红框中的对勾即可以实现各组件的随意添加与移除。
HTC Vive Pro eye 眼动数据简单获取_第20张图片

读取数据

在Gaze Ray Sample v2组件中,挂载了当前脚本。HTC Vive Pro eye 眼动数据简单获取_第21张图片

在该脚本中,修改代码,结果为下面所示。
修改后,需要启动SRanipalRuntime,同时启动SteamVR,使得HMD正常运行。点击unity中的play,即可看到最终结果。

//========= Copyright 2018, HTC Corporation. All rights reserved. ===========
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Assertions;

namespace ViveSR
{
    namespace anipal
    {
        namespace Eye
        {
            public class SRanipal_GazeRaySample_v2 : MonoBehaviour
            {
                public int LengthOfRay = 25;
                [SerializeField] private LineRenderer GazeRayRenderer;
                private static EyeData_v2 eyeData = new EyeData_v2();
                private bool eye_callback_registered = false;
                //此处为增加的变量
                private float pupilDiameterLeft, pupilDiameterRight;
                private Vector2 pupilPositionLeft, pupilPositionRight;
                private float eyeOpenLeft, eyeOpenRight;
                private void Start()
                {
                    if (!SRanipal_Eye_Framework.Instance.EnableEye)
                    {
                        enabled = false;
                        return;
                    }
                    Assert.IsNotNull(GazeRayRenderer);
                }

                private void Update()
                {
                    if (SRanipal_Eye_Framework.Status != SRanipal_Eye_Framework.FrameworkStatus.WORKING &&
                        SRanipal_Eye_Framework.Status != SRanipal_Eye_Framework.FrameworkStatus.NOT_SUPPORT) return;

                    if (SRanipal_Eye_Framework.Instance.EnableEyeDataCallback == true && eye_callback_registered == false)
                    {
                        SRanipal_Eye_v2.WrapperRegisterEyeDataCallback(Marshal.GetFunctionPointerForDelegate((SRanipal_Eye_v2.CallbackBasic)EyeCallback));
                        eye_callback_registered = true;
                    }
                    else if (SRanipal_Eye_Framework.Instance.EnableEyeDataCallback == false && eye_callback_registered == true)
                    {
                        SRanipal_Eye_v2.WrapperUnRegisterEyeDataCallback(Marshal.GetFunctionPointerForDelegate((SRanipal_Eye_v2.CallbackBasic)EyeCallback));
                        eye_callback_registered = false;
                    }

                    Vector3 GazeOriginCombinedLocal, GazeDirectionCombinedLocal;

                    if (eye_callback_registered)
                    {
                        if (SRanipal_Eye_v2.GetGazeRay(GazeIndex.COMBINE, out GazeOriginCombinedLocal, out GazeDirectionCombinedLocal, eyeData)) { }
                        else if (SRanipal_Eye_v2.GetGazeRay(GazeIndex.LEFT, out GazeOriginCombinedLocal, out GazeDirectionCombinedLocal, eyeData)) { }
                        else if (SRanipal_Eye_v2.GetGazeRay(GazeIndex.RIGHT, out GazeOriginCombinedLocal, out GazeDirectionCombinedLocal, eyeData)) { }
                        else return;
                    }
                    else
                    {
                        if (SRanipal_Eye_v2.GetGazeRay(GazeIndex.COMBINE, out GazeOriginCombinedLocal, out GazeDirectionCombinedLocal)) { }
                        else if (SRanipal_Eye_v2.GetGazeRay(GazeIndex.LEFT, out GazeOriginCombinedLocal, out GazeDirectionCombinedLocal)) { }
                        else if (SRanipal_Eye_v2.GetGazeRay(GazeIndex.RIGHT, out GazeOriginCombinedLocal, out GazeDirectionCombinedLocal)) { }
                        else return;
                    }

                    Vector3 GazeDirectionCombined = Camera.main.transform.TransformDirection(GazeDirectionCombinedLocal);
                    GazeRayRenderer.SetPosition(0, Camera.main.transform.position - Camera.main.transform.up * 0.05f);
                    GazeRayRenderer.SetPosition(1, Camera.main.transform.position + GazeDirectionCombined * LengthOfRay);
                    //以下为新增的部分
					//pupil diameter 瞳孔的直径
                    pupilDiameterLeft = eyeData.verbose_data.left.pupil_diameter_mm;
                    pupilDiameterRight = eyeData.verbose_data.right.pupil_diameter_mm;

                    //pupil positions 瞳孔位置
                    //pupil_position_in_sensor_area手册里写的是The normalized position of a pupil in [0,1],给坐标归一化了
                    pupilPositionLeft = eyeData.verbose_data.left.pupil_position_in_sensor_area;
                    pupilPositionRight = eyeData.verbose_data.right.pupil_position_in_sensor_area;

                    //eye open 睁眼
                    //eye_openness手册里写的是A value representing how open the eye is,也就是睁眼程度,从输出来看是在0-1之间,也归一化了
                    eyeOpenLeft = eyeData.verbose_data.left.eye_openness;
                    eyeOpenRight = eyeData.verbose_data.right.eye_openness;

                    Debug.Log("左眼瞳孔直径:" + pupilDiameterLeft + " 左眼位置坐标:" + pupilPositionLeft + "左眼睁眼程度" + eyeOpenLeft);
                    Debug.Log("右眼瞳孔直径:" + pupilDiameterRight + " 右眼位置坐标:" + pupilPositionRight + " 左眼睁眼程度" + eyeOpenRight);

                }
                private void Release()
                {
                    if (eye_callback_registered == true)
                    {
                        SRanipal_Eye_v2.WrapperUnRegisterEyeDataCallback(Marshal.GetFunctionPointerForDelegate((SRanipal_Eye_v2.CallbackBasic)EyeCallback));
                        eye_callback_registered = false;
                    }
                }
                private static void EyeCallback(ref EyeData_v2 eye_data)
                {
                    eyeData = eye_data;
                }
            }
        }
    }
}

该方法有些取巧,前文提到的“邓布利多军”是通过新建脚本实现的该功能。笔者也在进一步的尝试中。

总结

笔者从一头雾水,到如今简单的实现了该功能,要感谢官方论坛中许多位前辈,以及官方技术人员的技术支持。同时也要感谢众多曾在网络上分享技术,发表见解的前辈们。

最后,再次感谢“邓布利多军”。希望有更多未来的开发者们,加入技术分享的大家庭中。

你可能感兴趣的:(VR,unity,vr)