[PICO VR眼镜]眼动追踪串流Unity开发与使用方法,眼动追踪打包报错问题解决(Eye Tracking)

前言

最近在做一个工作需要用到PICO4 Enterprise VR头盔里的眼动追踪功能,但是遇到了如下问题:

  1. 在Unity里面没法串流调试眼动追踪功能,根本获取不到Device,只能将整个场景build成APK,安装到头盔里,才能在代码里调用眼动追踪功能。这使得每次修改代码都要打包一次apk安装到头盔里,十分不方便,难以调试。
  2. PICO VR 官方提供的Eye Tracking教程里,获取到的眼睛朝向和位置是相对于Head这个位置的,而不是相对于XR Origin下的Camera的位置,这使得API不能直接拿来使用。
  3. Unity在引入PICO VR眼动跟踪代码后,编译时点击"build and run"后,会报错"NullReferenceException: Object reference not set to an instance of an object"

鉴于以上问题网络上均没有人进行解答,以及个人没能得到PICO官方的技术支持情况下(三周内发了2封技术工单邮件+2次催线上客服),遂打算自己捣鼓一下写篇教程。

(点名批评PICO官方的技术支持不回复邮件的问题,明明就几个特别简单的问题,但是官方承诺的3-5个工作日内回复并没有做到,等了三周一封邮件也没回,这个过程还问了客服,客服表示会催技术人员回复,但等了一周半也没看到,放弃了orz)

更新补充:据说在PICO企业版官网提交企业版工单能得到官方较快的回复。

1. 如何在代码里使用眼动跟踪,并转换成世界坐标 或 摄像机坐标系

首先,检查眼动追踪是否能正常工作:

c 复制代码
void Start()
{
	CheckEyeTracking();

}
private void CheckEyeTracking()
{
    //Start PICO Eye Tracking Service
    TrackingStateCode trackingState;
    trackingState = (TrackingStateCode)PXR_MotionTracking.WantEyeTrackingService();
    Debug.Log("告知PICO想要眼动跟踪服务 trackingState: " + trackingState.ToString());

    //Check Eye Tracking able or not
    EyeTrackingMode eyeTrackingMode = EyeTrackingMode.PXR_ETM_NONE;
    bool supported = false;
    int supportedModesCount = 0;
    trackingState = (TrackingStateCode)PXR_MotionTracking.GetEyeTrackingSupported(ref supported, ref supportedModesCount, ref eyeTrackingMode);
    Debug.Log("检查是否有眼动跟踪功能 trackingState: "+ trackingState.ToString()+"  code  "+ supported);

    // Get Eye Tracking State
    bool tracking = true;
    EyeTrackingState eyeTrackingState = new EyeTrackingState();
    trackingState = (TrackingStateCode)PXR_MotionTracking.GetEyeTrackingState(ref tracking, ref eyeTrackingState);
    Debug.Log("获取当前眼动跟踪状态 trackingState: " + trackingState.ToString());

    // Start Eye Tracking
    //这里要严谨点的话,应该要加上if条件判断是否有眼动跟踪功能,再选择开启该功能
    EyeTrackingStartInfo info = new EyeTrackingStartInfo();
    info.needCalibration = 1;
    info.mode = eyeTrackingMode;
    trackingState = (TrackingStateCode)PXR_MotionTracking.StartEyeTracking(ref info);
    Debug.Log("开始眼动跟踪状态 trackingState: " + trackingState.ToString());

    //Get Eye Tracking Data
    //获取眼动跟踪数据
    EyeTrackingDataGetInfo eyeData = new EyeTrackingDataGetInfo();
    eyeData.displayTime = 0;
    eyeData.flags = EyeTrackingDataGetFlags.PXR_EYE_DEFAULT
    | EyeTrackingDataGetFlags.PXR_EYE_POSITION
    | EyeTrackingDataGetFlags.PXR_EYE_ORIENTATION;
    EyeTrackingData eyeTrackingData = new EyeTrackingData();
    trackingState = (TrackingStateCode)PXR_MotionTracking.GetEyeTrackingData(ref eyeData, ref eyeTrackingData);
    Debug.Log("eyeData:  "+ eyeData.ToString() + " TrackingData:  " + eyeTrackingData.ToString());

  
}

接下来,每帧都获取眼动数据,并将眼睛位置和眼睛朝向数据转换成世界坐标

c 复制代码
public Transform origin;//在Unity主界面把XR Origin拖进来即可
private Matrix4x4 headPoseMatrix,originPoseMatrix;
private Vector3 combineEyeGazeVector, combineEyeGazeOriginOffset, combineEyeGazeOrigin;
private Vector3 combineEyeGazeVectorInWorldSpace, combineEyeGazeOriginInWorldSpace;
void Update()
{
	GetEyeTrackingData();
}

private void GetEyeTrackingData()
{
		//获取head的局部转世界矩阵,该矩阵点乘head局部坐标系下坐标,则能转换为世界坐标系下的坐标
        PXR_EyeTracking.GetHeadPosMatrix(out headPoseMatrix);	
        //获取双眼(取中间位置)位于Head坐标系下的朝向信息GazeVector
        PXR_EyeTracking.GetCombineEyeGazeVector(out combineEyeGazeVector);
        //获取双眼(取中间位置)位于Head坐标系下的位置信息GazePoint
        PXR_EyeTracking.GetCombineEyeGazePoint(out combineEyeGazeOrigin);
        
        //获取眼睛的世界坐标
        combineEyeGazeOriginInWorldSpace = originPoseMatrix.MultiplyPoint(headPoseMatrix.MultiplyPoint(combineEyeGazeOrigin));
        //获取眼睛的朝向信息
        combineEyeGazeVectorInWorldSpace = originPoseMatrix.MultiplyVector(headPoseMatrix.MultiplyVector(combineEyeGazeVector));
		
		//highlighArea是我添加的一个手电筒高亮区域,能让用户更直观地查看眼睛看向位置
        highlighArea.transform.position = combineEyeGazeOriginInWorldSpace ;
        //LookRotation默认是以z轴旋转,如果要以y轴旋转的话可以在后面加上 ,Vector3.up
        highlighArea.transform.rotation = Quaternion.LookRotation(combineEyeGazeVectorInWorldSpace);
/*        RaycastHit hit;
        if (Physics.Raycast(highlighArea.transform.position, combineEyeGazeVectorInWorldSpace, out hit, 1000f))
        {
            highlighArea.transform.LookAt(hit.point);
        }*/
}

简易效果图(透明圈处是一个作为highlighArea的圆锥区域):

至此,便已经能获取到PICO VR里眼动追踪的位置和朝向信息。

2. "Build and Run"报错

引入EyeTracking代码后,发现Build and Run时会报以下错误:

这是因为run的话会导致Unity找不到可供眼动追踪的设备,导致编译报错。

解决方法,只点击Build,而不是Build and Run:

Build完后,打开Pico Developer Center,依次点击"应用管理"->"安装包"->"安装本地应用":

选择build好的apk即可,后续便能直接在眼镜里运行:

3.眼镜内调试的Trick

对于这种要build到眼镜里才能调试的功能,可以利用Unity提高的UI->Text组件进行调试。

即,在Unity场景下创建一个UI->Text对象,然后把对象拖到下面脚本的GazeDebugText变量上,便能在眼镜里看到相应数据输出了

c 复制代码
public TMP_Text GazeDebugText;
private void GetEyeTrackingData()
{
	GazeDebugText.text = combineEyeGazeOrigin.ToString("F3");//F3是指精确到小数点后3位
}

效果:

4.如何在Unity里串流调用眼动追踪功能

B站视频:使用PICO头显在VR串流环境下进行眼动追踪

我个人是在看了这个视频后才知道PICO VR也能在Unity下串流使用眼动跟踪功能,但我没有进一步深入探究,据该视频UP主表示,PICO neo3 pro eye是可以串流的,该方法理应也适用于其他头盔。

在此处也表达下对UP主的感谢,在后台私信耐心细致替我解答问题。

原理:串流使用pico的眼动追踪功能,需要下载特定的串流软件和PICO系统版本,电脑端上用的是PICO企业版串流工具(普通版的串流工具不知道可不可以):

在PICO端上则是自动保持串流软件更新到最新版本。

build成apk安装包之后,调用的SDK是正常使用的;串流时候的SDK会随business streaming一起安装,和apk的SDK不一样

business streaming安装后,sdk里会有一个用qt写的测试程序,可以试试看串流后眼动跟踪是否正常,测试程序不需要用steam;但是对头显的固件版本有要求:

如果经过测试后,eye tracking data什么的没有问题,后续可以串流Unity基于OpenXR进行开发。

相关推荐
WarPigs6 小时前
Unity阴影
unity·游戏引擎
一只一只7 小时前
Unity之Invoke
unity·游戏引擎·invoke
技术小甜甜8 小时前
【Godot】【入门】信号系统从 0 到 1(UI/玩法彻底解耦的通用写法)
ui·游戏引擎·godot
技术小甜甜9 小时前
【Godot】【入门】节点生命周期怎么用(避免帧循环乱写导致卡顿的范式)
游戏引擎·godot
tealcwu10 小时前
【Unity踩坑】Simulate Touch Input From Mouse or Pen 导致检测不到鼠标点击和滚轮
unity·计算机外设·游戏引擎
ThreePointsHeat10 小时前
Unity WebGL打包后启动方法,部署本地服务器
unity·游戏引擎·webgl
erxij10 小时前
【游戏引擎之路】《古今东西4》正式立项——新的一年,开始长征
游戏引擎
迪普阳光开朗很健康10 小时前
UnityScrcpy 可以让你在unity面板里玩手机的插件
unity·游戏引擎
陈言必行1 天前
Unity 之 设备性能分级与游戏画质设置与设备自动适配指南
游戏·unity·游戏引擎
liuhaikang1 天前
鸿蒙VR视频播放库——md360player
音视频·vr·harmonyos