奇遇 Unity XR SDK 开发文档 - iQIYIVR/QIYU_VR_v3 GitHub Wiki

一、Unity XR SDK

1.1 简介

本文档介绍在Unity游戏开发引擎环境下,开发者使用奇遇 Unity XR SDK(以下简称SDK)制作运行在奇遇VR一体机设备上的VR应用和游戏。

1.2 主要特性

  • 基于UnityXR Plug-in标准接口框架,提供VR应用开发包。
  • 采用先进的由内而外的追踪系统,达到毫米级精度。
  • 奇遇3手柄控制器为六自由度控制器,实现直观自然的手部运动仿真。
  • Singlepass、Foveation Rendering、Vulkan等高级渲染功能提高性能。
  • 高质量3D音频,为数字音频工作站提供插件。
  • 提供免费的DLC上传和下载服务,帮助开发者减少APK下载大小。

1.3 开发环境要求

软件要求
Unity Editor 必须使用unity2019.4.22f1以上版本
Android SDK API Level 26(Android 8.0) 及以上
JDK JDK 1.8.0及以上
硬件要求
奇遇3一体机 QIYU OS v1.6.1及以上

1.4 Unity Package 目录说明

SDK通过zip包格式提供,开发者导入后可看到如下目录:

  • Assets目录:SDK的模型,材质,shader等资源
  • Editor目录:SDK用编辑器工具
  • Rumtime目录:SDK相关运行时脚本
  • Samples目录:SDK相关Samples例子工程
  • package.json文件:SDK配置文件,导入SDK的入口

二、奇遇3 VR一体机

奇遇3是爱奇艺智能科技有限公司(简称:爱奇艺智能)研发的6自由度一体式VR设备,搭载高通XR2芯片,采用基于计算机视觉技术的Inside-out毫米级定位追踪方案,拥有一对6自由度定位追踪的手柄控制器。下表列举了奇遇3设备的基本参数:

屏幕 单眼分辨率2160 x 2160,刷新率 90HZ
芯片 高通骁龙 XR2
内存 8GB
存储 128GB
网络 WiFi 6
蓝牙 BT 5.1
声音 双麦克风录音,双扬声器
追踪摄像头 四个语言追踪摄像头
电池 5500mAh
接口 USB Type-C,3.5mm 耳机插口
定位追踪 头部和手部Inside-Out毫米级定位追踪
手部追踪范围垂直方向160°、水平方向160°
控制器 两个6自由度手柄控制器
视场角 95°

三、快速入门

了解如何导入SDK软件包,以及如何针对您的开发环境进行基本测试。

  • 我们假设您已经了解了什么是虚拟现实及其使用的术语。 如果不是,请检查Unity XR词汇表。
  • 强烈建议您在使用SDK之前,先阅读Unity XR文档,然后正确安装Unity Editor和ADB调试工具。

步骤1:

打开Unity Editor,新建工程

图3.1 创建工程

步骤2:

修改Platform为Android平台

图3.2 修改Platform

步骤3:

导入SDK PackageManager选择加号->Add package from disk按钮加载上面解压目录的package.json文件,XR插件就会自动导入。

图3.3 导入SDK

步骤4:

导入后,点击“Yes”,启用Unity new input System,Unity Editor会自动重启。

图3.4 启用Unity new input System

步骤5:

导入以后会看到Qiyu XR Plugin;如果有需要,点击Import into Project导入Sample工程。

图3.5 导入Samples

步骤6:

新建场景,创建XRRig,XRRig提供的功能参见4.1UnityXR介绍。

图3.6 创建XR Rig

步骤7:

必须在ProjectSettings—>XRPlug-in Management里的勾选“Qiyu”选项

图3.7 开启Qiyu

步骤8:

设置推荐配置,可通过“Menu/Qiyu/Modify Player Settings”一键自动设置为推荐配置。

图3.8 常用菜单功能

配置项 推荐值
GraphicsAPIs OpenGLES3
ApiCompatibilityLevel .NET 4.x
minSdkVersion AndroidApiLevel26
Scripting Backend IL2CPP
Target Architectures ARM64
Default Orientation LandscapeLeft
vSyncCount Every V Blank

表3.1 工程推荐配置

步骤9:

如果需要使用自定义AndroidManifast.xml文件,可以点击Qiyu->Tools->Create AndroidManifast按钮创建SDK需要的默认配置模板,在此基础上进行修改。不需要的时候可以点击Delete AndroidManifast按钮进行删除。

图3.9 创建AndroidManifast配置

步骤10:

打包,安装进一体机运行

四、功能介绍

4.1 UnityXR

奇遇UnityXR SDK基于UnityXR Plug-in Framework(https://docs.unity3d.com/Manual/XR.html)开发,目前支持以下UnityXR子系统,具体接口说明参见UnityXR说明文档:

Subsystem Reference
XRDisplaySubsystem https://docs.unity3d.com/Manual/xrsdk-display.html
XRInputSubsystem https://docs.unity3d.com/Manual/xrsdk-input.html
XRNodeState https://docs.unity3d.com/ScriptReference/XR.XRNodeState.html
XRSettings https://docs.unity3d.com/ScriptReference/XR.XRSettings.html

4.2 XRRig

本指南介绍了UnityXR为开发者提供的统一XR Rig prefab及功能。

图4.1 XR Rig配置面板

4.2.1 Tracking Origin Mode

用于设置追踪原地的模式。 Device:以头显位置和方向作为参考进行追踪,Recenter会复原虚拟相机的位置 Floor:以头显检测到的地面为参考面进行追踪,Recenter不会改变虚拟相机的高度 注:对于多场景应用中,XRRig的使用注意,参见7.1 XRRig使用注意

4.3 QiyuManager

本指南介绍了奇遇为开发者提供的一些常用功能,如果需要,请将QiyuManager prefab拖进场景。

图4.2 QiyuManager 预制体

图4.3 QiyuManager 配置面板

4.3.1 Foveation Level

详细说明参见4.6.1注视点渲染

4.3.2 Eye Resolution Scale Factor

设置RenderTexture缩放系数,默认为1(推荐),即采用系统提供的尺寸,开发者可根据场景需要调整。如果小于1,画面清晰度会降低,但帧率会提升,功耗降低;如果大于1,画面清晰度会进一步提升,但是帧率会降低,功耗和内存会增加。建议最大值不要超过1.5。

4.3.3 使用3Dof模式

  • HMD 3Dof:MainCamera的Tracked Pose Driver组件的Tracking Type改为Rotation Only。

图4.4 头3Dof 模式

  • Controller 3Dof:调用接口Utils.SetTrackingPosition(false)。

4.4 输入

本指南描述了SDK支持的输入交互功能。

4.4.1 通用输入接口

UnityEngine.XR.InputDevices封装了所有的设备输入接口,目前包括手柄控制器的输入接口。开发者可查阅XR Interaction Toolkit教程(https://docs.unity3d.com/Packages/[email protected]/manual/index.html)接入输入功能。

4.4.2 奇遇3手柄控制器

如果开发者需要在场景中使用奇遇3手柄模型,可将QIYI3_Handle拖进XRcontroller指定Model Prefab位置。

图4.5 奇遇3手柄按键示意图

图4.6 使用奇遇3手柄模型

4.4.3 VR输入法键盘

奇遇 SDK中提供了VR输入法键盘,方便开发人员将虚拟键盘放置在应用程序中。

  1. 将VRInputField Prefab 拖动到UI中。
  2. 单击控件以弹出虚拟键盘。
  3. 可参考SGKeyboard.unity 示例场景。

图4.7 VR输入法键盘 4. 开发者可根据需要调节Controller参数,为了提升键盘操作的体验,推荐Device-based模式下,手柄按键阈值使用0.5。

图4.8 XR Controller属性面板 5. 设置左右手输入,如果只需要使用其中一个,可以将另外一个值NULL。

图4.9 输入法配置面板 6. 注意必须把ProjectSettings里的Active Input Handling设置为Both

图4.10 Both设置

4.5 声音

本指南介绍了在Unity中创建引人入胜的VR音频体验的开发资源。

4.5.1 Unity AudioSource

  • 添加AudioSource组件,设置3D音效

  • 开发者可在ProjectSettings-Audio中配置空间音频插件

4.5.2 Resonance Audio插件

Resonance Audio是一个多平台的空间音频SDK,可提供高保真度、强大的空间音频技术,对于VR游戏和视频的真实体验有很大的帮助。 插件下载地址:https://resonance-audio.github.io/resonance-audio/develop/unity/getting-started

图4.11 空间音频插件设置

4.6 高级渲染功能

本指南介绍了有助于提升性能的高级渲染功能。

4.6.1 注视点渲染

注视点渲染(Foveation Rendering)可以优化VR场景的渲染,该技术通过为视野中心提供全分辨率(无损),降低周边视野(人眼焦点区域之外)分辨率的方式来达到优化渲染的目的。静态注视点渲染(FFR)是指将视野焦点固定在视口中心位置,实现从中心向周围逐渐降低清晰度的效果。

图4.12 Foveation Rendering原理示意图 开启和配置"Foveated Rendering"选项如下图所示,在QiyuManager对象上QiyuManager.cs脚本中控制

图4.13 Foveation Rendering 配置界面 Foveation Level等级越高帧率越高,但是像素损失越多,会影响边缘区域清晰度,请根据渲染需要选择合适的等级。开发者也可以通过5.1提供的接口设置注视点渲染级别或自定义注视点渲染参数。

4.6.2 Multi View (Single Pass)

UnityXR提供了Single Pass立体渲染技术(https://docs.unity3d.com/Manual/SinglePassStereoRendering.html),通过一个Camera实现立体渲染,可降低50%的DrawCall,对于CPU占用高的场景帧率提升明显。 开发者可通过Project Settings->XR Plug-in Management->Qiyu,选择MultiView,该选项全局生效。

图4.14 MultiView选项

4.6.3 Vulkan 图形接口

Vulkan是一种现代图形API,与许多方面与OpenGL ES类似(OpenGL ES是奇遇3应用程序开发中使用的主要图形API),可以带来更好的CPU渲染性能,但无法提高GPU性能。 具体打开Vulkan设置的步骤如下:

  1. 打开Unity Editor > Build Settings > Player Settings > Settings for Android
  2. 找到Other Settings,勾掉 Auto Graphic API选项,在Graphic API下方添加Vulkan。
  3. 在Graphic API下将Vulkan排到OpenGLES上面。

图4.15 启用Vulkan Vulkan已知问题

  • 在我们的性能测试中,发现Vulkan API 大致相当于OpenGL ES性能,可能某些游戏的性能有一些提升,如果您的应用程序使用Vulkan时,相比OpenGL ES,性能有下降,请向我们提交错误,我们将评估修复。
  • Vulkan API目前为实验性质的功能,可能引起应用程序的中断或崩溃,如果您的应用程序使用Vulkan后没有出现问题,我们鼓励将项目升级到Vulkan。
  • Vulkan API不支持MutilView和FFR功能。

4.7 菜单功能

本指南介绍了奇遇提供的简化开发者部分工作的功能。提供入口Menu-Qiyu

图4.16 菜单功能

4.7.1 Modify Player Setting

一键自动修改工程配置至推荐配置,详细介绍参见快速入门步骤8

4.7.2 Create/Delete Manifest

如果开发者需要在Manifest文件中加入额外的内容(比如权限),可通过“Menu-Qiyu-Tools-Create Manifest”一键自动生成Manifest文件。详细介绍参见快速入门步骤9

五、接口说明

5.1 注视点渲染

注视点相关API

  • 设置注视点等级
 Utils.SetFoveationLevel((int)FoveationLevel.High);
  • 设置注视点参数
Utils.SetFoveationParamets(30,30,30,30);

5.2 Boundary APIs

5.2.1 UnityXR

UnityXR提供了以下接口:

  • 获取围栏点数据
UnityEngine.XR.InputDevices.GetDeviceAtXRNode(UnityEngine.XR.XRNode.Head).subsystem.TryGetBoundaryPoints(List<Vector3> boundaryPoints)
  • 围栏改变监听事件
UnityEngine.XR.InputDevices.GetDeviceAtXRNode(UnityEngine.XR.XRNode.Head).subsystem.boundaryChanged

5.2.2 Qiyu

对于UnityXR未提供的接口,QiyuBoundary类开放了更多功能的接口,供开发者使用。

  • 获取围栏是否是自定义围栏
public bool GetConfigured()
  • 测试围栏中的Node结果
public BoundaryTestResult TestNode(Node node)
  • 测试围栏中的Point结果
public QiyuBoundary.BoundaryTestResult TestPoint(Vector3 point)
  • 获取所有围栏点集
public Vector3[] GetGeometry()
  • 获取围栏尺寸范围
public Vector3 GetDimensions()
  • 获取围栏是否可见
public bool GetVisible()
  • 设置围栏常驻显示(系统设置中的围栏开关会覆盖此接口)
public void SetVisible(bool value)

六、平台解决方案

6.1 初始化

如果您需要使用奇遇平台功能,必须先按照以下内容初始化奇遇SDK:

API:

/// <summary> 
/// 初始化SDK 
/// </summary> 
/// <param name="app_id">APPID</param> 
/// <param name="developer_id">DeveloperID</param> 
/// <param name="app_secret">App 秘钥</param> 
/// <param name="sign_key">App 接口签名Key</param> 
public static void InitQiyuSDK(string app_id, string developer_id, string app_secret, string sign_key) 
{ 
    QiyuSDKCorePlugins.QVR_InitQiyuSDK(app_id, developer_id, app_secret, sign_key); 
}

参数说明:

  • developer_id、app_secret、sign_key:将在商务为您分配开发者账号信息后自动生成,你可在登录奇遇开发者网站后,点击网站右上角“开发者名称”进入账户页面查看。
  • app_id:请您在登录奇遇开发者网站后,点击“发布管理—应用管理”创建您的应用,系统将会给每个应用生成唯一app_id。

注意:初始化接口必须在Start函数或后面的时机调用,不能在Awake函数里调用否则会崩溃 Demo :

      //First, you need to monitor the SDK initialization result 
        QiyuMessageListener.AddListener(QiyuMessageCode.QiyuSdkInit, ret => 
        { 
            QiyuPlugin.MessageResult<QiyuPlugin.SDKInit> msg = ret[0] as QiyuPlugin.MessageResult<QiyuPlugin.SDKInit>; 
            if (msg.IsSuccess()) 
            { 
      
            }  
        });
        
        //Initialize SDK 
        QiyuPlugin.InitQiyuSDK( 
            "70519169", 
            "820903", 
            "04c8f3a2d398a09debdb34902f278e2a", 
            "bc85d655c3416d5abdd98d4f7184fa22");

app初始化完毕后,开发者应该首先初始化sdk,来验证开发者身份,所有平台接口都需要身份验证成功后调用。否则会以下监听代码中返回初始化失败:

   QiyuMessageListener.AddListener(MessageCode.QiyuSdkInit, ret => 
    { 
        QiyuPlugin.MessageResult<QiyuPlugin.SDKInit> msg = ret[0] as QiyuPlugin.MessageResult<QiyuPlugin.SDKInit>; 
        if (msg.IsSuccess()) 
        { 
            ///没有初始化成功的时候会调用
        }  
    });

6.2 奇遇账户

在应用程序中获取奇遇用户的帐户状态和信息。 API:

/// <summary> 
/// 获取Qiyu账户是否登录 
/// </summary 
public static bool IsQiyuAccountLogin() 
{ 
    return QVRSDKCorePlugins.QVR_IsAccountLogin() == (int)Bool.True; 
}

/// <summary> 
/// 获取Qiyu账户信息 
/// </summary> 
/// <param name="callback">请求的回调函数</param> 
public static void GetQiyuAccountInfo(RequestCallback callback) 
{ 
   QiyuSDKCorePlugins.QVR_GetQiyuAccountInfo(QiyuMessageManager.AddRequest(callback)); 
}

Demo:

// Get Qiyu Account information 
QiyuPlugin.GetQiyuAccountInfo( 
RequestCallbackByJson<QiyuPlugin.MessageResult<QiyuPlugin.QiyuAccountInfo>>.Create((QiyuPlugin.MessageResult<QiyuPlugin.QiyuAccountInfo> msg) => 
{ 
    if (msg.IsSuccess()) 
    { 
        Debug.Log("GetQiyuAccountInfo= " +msg.data.icon +"  "+ msg.data.name +"  "+ msg.data.uid); 
    } 
}));

开发者在获取奇遇账号信息时,应先判断是否已经登录奇遇账号,只有登录成功后,才会获取到账户信息。

    if (QiyuPlugin.IsQiyuAccountLogin()) 
    { 
        QiyuPlugin.GetQiyuAccountInfo( 
       RequestCallbackByJson<QiyuPlugin.MessageResult<QiyuPlugin.QiyuAccountInfo>>.Create((QVRPlugin.QVRMessageResult<QVRPlugin.QiyuAccountInfo> msg) => 
       { 
           if (msg.IsSuccess()) 
           { 
               uid.text = msg.data.uid; 
               name.text = msg.data.name; 
               pic.text = msg.data.icon; 
           } 
       })); 
    } 
    else { 
        //跳转到Home进行登录 
        QiyuPlugin.LaunchHome("show", "login"); 
    }

6.3 深度链接

当您有两个独立的应用程序时:例如一个单人游戏和一个多人游戏。如果你想要在两个应用程序中实现应用程序深度链接,用户可以从单人应用程序加入多人游戏应用。 深度链接需要在发出请求的应用程序和目标应用程序中进行集成。接下来的部分将描述这两个应用程序所需的实现。 API:

/// <summary> 
/// 打开其他应用 
/// </summary> 
/// <param name="app_id">应用id</param> 
/// <param name="key">深度连接Key</param> 
/// <param name="value">深度连接value</param>
    public static void LaunchOtherApp(string app_id, string key, string value) 
    { 
            QiyuSDKCorePlugins.QVR_LaunchOtherApp(app_id, key, value); 
    }

/// <summary> 
/// 获取深度连接信息 
/// </summary> 
/// <param name="callback">回调函数</param> 
public static void GetDeepLink(RequestCallback callback) 
{ 
        QiyuSDKCorePlugins.QVR_GetDeepLink(QiyuMessageManager.AddRequest(callback)); 
}

Demo:

  • 从A app 打开 B app, 调用LaunchOtherApp 并传入深度链接参数。
  • B app 在初始化完毕之后,调用GetDeepLink 函数获取之前传入的深度链接参数。
QiyuPlugin.LaunchOtherApp("70519169", "show", "1"); 

QiyuPlugin.GetDeepLink( 
    RequestCallbackByJson<QiyuPlugin.QiyuMessageResult<QiyuPlugin.DeepLinkParam>>.Create((QiyuPlugin.MessageResult<QiyuPlugin.DeepLinkParam> ret) => 
{ 

}));

6.4 DLC

奇遇提供免费的DLC上传和下载服务,以帮助开发者减少APK的包体大小和用户的下载更新成本。

  • DLC上传 当您的安装包过大,用户下载应用的成本过高,例如一些图片、视频等需要和应用程序关联,奇遇为您提供了更简单的DLC资源文件上传(DLC文件必须是压缩文件,可最多支持上传5个,每个文件最大3.5G),您可以自由定义在哪些场景下需要去下载DLC文件,以减少APK大小。 同时DLC会关联APK的版本,那么该APK版本下的DLC可以支持随时更新,无需更新APK,以减少用户的下载更新成本。
  • 获取DLC
  1. 您上传DLC文件后,您可以通过以下接口获取DLC信息 API:
/// <summary> 
/// 获取DLC更新信息 
/// </summary> 
/// <param name="app_id">AppID</param> 
/// <param name="curVersion">当前应用版本</param> 
/// <param name="callback">回调函数</param> 
public static void GetAppUpdateInfo(string app_id, string curVersion, RequestCallback callback) 
{ 
        QiyuSDKCorePlugins.QVR_GetAppUpdateInfo(QiyuMessageManager.AddRequest(callback), app_id, curVersion); 
}

Demo:

QiyuPlugin.GetAppUpdateInfo( 
    "70519169", 
    "1", 
   RequestCallbackByJson<QiyuPlugin.MessageResult <QiyuPlugin.AppUpdateInfo>>.Create((QiyuPlugin.MessageResult<QiyuPlugin.AppUpdateInfo> msg1) => 
   { 
       if (msg1.IsSuccess()) 
       { 
      
       }  
   }));
  1. 获取到DLC信息后,由开发者根据DLC信息完成下载资源文件,并定义下载更新的场景和交互。此时用户在打开应用时,才能获取应用的完整功能和体验。

七、开发指南及常见问题

7.1 XR Rig的使用注意

  1. 如果游戏中有多个场景,每个场景都放置XR Rig,那么每次切换到新场景,都会自动重置position和rotation,TrackingOriginMode也会重新计算。
  2. 如果想要整个App只有一个XR Rig,可以制作一个启动场景放入XRRig,或者单例,然后调用GameObject.DontDestroyOnLoad让XR Rig保持不销毁。其他场景不放置XR Rig,这种情况在切换场景的时候可以保证position和rotation不被重置,TrackingOriginMode也不会重新计算。
  3. 如果不销毁XR Rig,需要同时保证XR Interaction Manager,QiyuManager同时不被销毁。
  4. 在使用单XR Rig的时候,如果跳转到其他场景中射线无法交互,则需要通过代码设置Canvas上的Event Camera,另外进入新的场景后需要重新初始化XRRayInteractor(可能是Unity的bug),这里我们只需要做一下隐藏和显示就可以了。具体示例如下,将该脚本挂载到UI Canvas上。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class QiyuCanvasController : MonoBehaviour
{
    Canvas canvas;
    private void Awake()
    {
        canvas = GetComponent<Canvas>();
        canvas.renderMode = RenderMode.WorldSpace;
        canvas.worldCamera = Camera.main;
    }
    // Start is called before the first frame update
    IEnumerator Start()
    {
        XRRayInteractor[] xRRayInteractor = FindObjectsOfType<XRRayInteractor>();
        foreach (XRRayInteractor xrRay in xRRayInteractor)
        {
            xrRay.gameObject.SetActive(false);
            yield return new  WaitForEndOfFrame();
            xrRay.gameObject.SetActive(true);
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

7.2 老版本迁移

  1. 奇遇Unity SDK v0.4版本及以前版本迁移到UnityXR SDK v1.0.0版本时,必须删除老的SDK所有文件(QYVRSDK、QIVR Sample),再通过Unity Package Manager导入新的XR插件。
  2. 1.0版本统一规范了XR接口名称,在删除掉老版本SDK、导入XR sdk后,如果出现unity Editor Log报错,需要根据本开发文档和UnityXR开发文档,替换相应的接口名称。
  3. 挂在原“QVR Camera”预制体及其子物体上的脚本、组件,请根据需要移至“XR Rig”预制体上。

7.3 多语言AppName配置

如果需要不同系统语言下显示不同AppName,需要在打包的时候导入Android的多语言配置文件,在每个语言string.xml里配置不同app_name字段,多语言package例子可以从这里下载:

⚠️ **GitHub.com Fallback** ⚠️