一、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输入法键盘,方便开发人员将虚拟键盘放置在应用程序中。
将VRInputField Prefab 拖动到UI中。
单击控件以弹出虚拟键盘。
可参考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设置的步骤如下:
打开Unity Editor > Build Settings > Player Settings > Settings for Android
找到Other Settings,勾掉 Auto Graphic API选项,在Graphic API下方添加Vulkan。
在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
围栏改变监听事件
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:
///
/// 初始化SDK
///
/// APPID
/// DeveloperID
/// App 秘钥
/// App 接口签名Key
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
if (msg.IsSuccess())
{
}
});
//Initialize SDK
QiyuPlugin.InitQiyuSDK(
"70519169",
"820903",
"04c8f3a2d398a09debdb34902f278e2a",
"bc85d655c3416d5abdd98d4f7184fa22");
app初始化完毕后,开发者应该首先初始化sdk,来验证开发者身份,所有平台接口都需要身份验证成功后调用。否则会以下监听代码中返回初始化失败:
QiyuMessageListener.AddListener(MessageCode.QiyuSdkInit, ret =>
{
QiyuPlugin.MessageResult
if (!msg.IsSuccess())
{
///没有初始化成功的时候会调用
}
});
6.2 奇遇账户
在应用程序中获取奇遇用户的帐户状态和信息。
API:
///
/// 获取Qiyu账户是否登录
///
public static bool IsQiyuAccountLogin()
{
return QVRSDKCorePlugins.QVR_IsAccountLogin() == (int)Bool.True;
}
///
/// 获取Qiyu账户信息
///
/// 请求的回调函数
public static void GetQiyuAccountInfo(RequestCallback callback)
{
QiyuSDKCorePlugins.QVR_GetQiyuAccountInfo(QiyuMessageManager.AddRequest(callback));
}
Demo:
// Get Qiyu Account information
QiyuPlugin.GetQiyuAccountInfo(
RequestCallbackByJson
{
if (msg.IsSuccess())
{
Debug.Log("GetQiyuAccountInfo= " +msg.data.icon +" "+ msg.data.name +" "+ msg.data.uid);
}
}));
开发者在获取奇遇账号信息时,应先判断是否已经登录奇遇账号,只有登录成功后,才会获取到账户信息。
if (QiyuPlugin.IsQiyuAccountLogin())
{
QiyuPlugin.GetQiyuAccountInfo(
RequestCallbackByJson
{
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:
///
/// 打开其他应用
///
/// 应用id
/// 深度连接Key
/// 深度连接value
public static void LaunchOtherApp(string app_id, string key, string value)
{
QiyuSDKCorePlugins.QVR_LaunchOtherApp(app_id, key, value);
}
///
/// 获取深度连接信息
///
/// 回调函数
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
{
}));
6.4 DLC
奇遇提供免费的DLC上传和下载服务,以帮助开发者减少APK的包体大小和用户的下载更新成本。
DLC上传
当您的安装包过大,用户下载应用的成本过高,例如一些图片、视频等需要和应用程序关联,奇遇为您提供了更简单的DLC资源文件上传(DLC文件必须是压缩文件,可最多支持上传5个,每个文件最大3.5G),您可以自由定义在哪些场景下需要去下载DLC文件,以减少APK大小。
同时DLC会关联APK的版本,那么该APK版本下的DLC可以支持随时更新,无需更新APK,以减少用户的下载更新成本。
获取DLC
您上传DLC文件后,您可以通过以下接口获取DLC信息
API:
///
/// 获取DLC更新信息
///
/// AppID
/// 当前应用版本
/// 回调函数
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
{
if (msg1.IsSuccess())
{
}
}));
获取到DLC信息后,由开发者根据DLC信息完成下载资源文件,并定义下载更新的场景和交互。此时用户在打开应用时,才能获取应用的完整功能和体验。
七、开发指南及常见问题
7.1 XR Rig的使用注意
如果游戏中有多个场景,每个场景都放置XR Rig,那么每次切换到新场景,都会自动重置position和rotation,TrackingOriginMode也会重新计算。
如果想要整个App只有一个XR Rig,可以制作一个启动场景放入XRRig,或者单例,然后调用GameObject.DontDestroyOnLoad让XR Rig保持不销毁。其他场景不放置XR Rig,这种情况在切换场景的时候可以保证position和rotation不被重置,TrackingOriginMode也不会重新计算。
如果不销毁XR Rig,需要同时保证XR Interaction Manager,QiyuManager同时不被销毁。
在使用单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.renderMode = RenderMode.WorldSpace;
canvas.worldCamera = Camera.main;
}
// Start is called before the first frame update
IEnumerator Start()
{
XRRayInteractor[] xRRayInteractor = FindObjectsOfType
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 老版本迁移
奇遇Unity SDK v0.4版本及以前版本迁移到UnityXR SDK v1.0.0版本时,必须删除老的SDK所有文件(QYVRSDK、QIVR Sample),再通过Unity Package Manager导入新的XR插件。
1.0版本统一规范了XR接口名称,在删除掉老版本SDK、导入XR sdk后,如果出现unity Editor Log报错,需要根据本开发文档和UnityXR开发文档,替换相应的接口名称。
挂在原“QVR Camera”预制体及其子物体上的脚本、组件,请根据需要移至“XR Rig”预制体上。
7.3 多语言AppName配置
如果需要不同系统语言下显示不同AppName,需要在打包的时候导入Android的多语言配置文件,在每个语言string.xml里配置不同app_name字段,多语言package例子可以从这里下载:
