Oculus Quest Development with UE4

Oculus Quest是 Oculus 发布的新一代支持 6DoF 的 VR 一体机设备,不需要连接 PC 以及额外的定位基站,而且支持 Guardian,当戴着头显走出定位边界时,头显中会立即显示现实中的画面,防止玩家误碰出现意外情况。
Oculus Quest使用两个 Pentile OLED 的屏幕,单眼分辨率为 1440x1600,刷新率为72Hz,使用的是arm 架构的高通骁龙 835 处理器,与两年前的 Android 旗舰级的处理器相同 (如小米 6、三星 S8)。
Quest 使用的是Oculus Insight(inside-out tracing) 定位方案,使用四枚摄像头进行位置追踪,分别位于头显面板的四角。
发布会时对 Oculus Insight 的介绍:Oculus Insight VR Positional Tracking System (Sep 2018)

以及国外的一个老哥对 Quest 追踪范围的测试视频:Quest Distance Test.

Oculus Quest64G 存储版本的售价为 399 刀,128G 的为 499 刀,不计税的价格大概是 3500;相比较 HTC 的同类新产品 (HTC Vive Focus) 是便宜了不少,与 PC-Base VR 相比那就更具优势了,还不需要一台高性能的主机,我觉得 6DoF 的 VR 一体机设备一定是未来的趋势!

国庆前的 OC6,Oculus 发布了 Oculus Link 和 finger tracking 两项技术,十分厉害,十分看好。

整套 Quest 设备的大小与 10.5 寸的 iPad 差不多,提个小包就能带走:

Quest 设备的参数细节不再多说,本篇文章的主要内容是使用 UE 来开发 Quest 项目时的环境部署、开发文档、调试工具以及额外的注意事项,会持续更新。

首先想要在 Quest 上打包测试应用需要先打开 Quest 的开发者模式。

首先用一根 Type-C 的数据线将 Quest 连接至电脑,在头显中会提示是否允许 USB 调试,勾选允许。
之后就可以在 PC 上用 Adb 连接到 Quest 了,Quest 上的系统也是基于 Android 的,可以使用 Adb 进行调试。

1
adb shell

就可以连接到 Quest 设备了,可以查看 /system/build.props 查看设备信息。我导出了一份:build.props

也可以直接使用 adb 来安装 apk:

1
adb install xxxx.apk

另外可以使用 SideQuest 这个开源工具在 PC 上管理基于 Android 的 VR 设备,当然 Quest 也适用,可以安装或者卸载软件。

Oculus Developer Mode

在进行真正的开发环境设置之前,需要将 Quest 开启 开发者模式 ,设置开发者模式的前提是需要在Oculus Dashboard 上加入或者 创建 开发者组织。

然后使用具有开发者组织的 Oculus 账号登陆移动端的OculusApp:

安装之后,登录账号,找到与账号关联的 Quest 设备,选择 更多设置

然后找到 开发者模式,启用即可。

启用之后就可以在 Quest 里的 Library 下看到Unknow Source,也就是未知来源应用了(后续测试安装的都在这个分类下)。

NVIDIA CodeWorks for Android

因为本质上 Quest 跑的就是 Android 系统,所以我们打包的游戏就是安卓上的 apk,那么为了能顺利打包成功 apk,则需要安装 Android 的开发环境。
我们需要安装 android-sdk/jdk/android-ndk/gradle/apache-ant 等一系列环境,不过一个好消息是,NVIDIA 有一个 NVIDIA CodeWorks for Android 能够极大简化安装 Android 开发环境的流程。

各个平台的 CodeWorks for Android 安装包可以从 UE 引擎的下列目录找到:

1
UE_4.23_Source\Engine\Extras\AndroidWorks\Win64

也可以从 NVIDIA 的 Download Center 下载。
启动安装,选择 安装目录 下载目录

安装完成之后打开安装路径(默认在C:\NVPACK\),启动Chooser.exe

选择 Standard 之后点击右下角的 Next 即可开始下载并安装。
鉴于国内的网络环境实在太差,有些库确实下载不下来,我把 Standard 下所有的都下载下来了,下载地址:CodeWorks_1R7,下载之后将其放到安装 CodeWorks 时选择的下载目录中,然后重新启动 Chooser.exe,点击Next 它会自己验证并且安装。

如果不想安装 CodeWorks,则可以仅仅只下载 Android 环境:NVPACK_1R7u1_20190923,然后把下面的环境变量添加进系统中。

安装完成之后会在系统中自动添加下列环境变量:

Environment var NAME Value
ANDROID_HOME C:\NVPACK\android-sdk-windows
ANDROID_NDK_ROOT C:\NVPACK\android-ndk-r14b
ANT_HOME C:\NVPACK\apache-ant-1.8.2
GRADLE_HOME C:\NVPACK\gradle-4.1
JAVA_HOME C:\NVPACK\jdk1.8.0_77
NDK_ROOT C:\NVPACK\android-ndk-r14b
NDKROOT C:\NVPACK\android-ndk-r14b
NVPACK_NDK_TOOL_VERSION 4.9
NVPACK_NDK_VERSION android-ndk-r14b
NVPACK_ROOT D:\NVPACK


这些环境变量在 UE 项目的 Project Setting-Android SDK 中被用到。

注意:最好确保环境变量的路径中没有特殊字符,纯数字 + 字母最好的。

Create Quest Project with UE4

首先启动 Unreal Engine,我使用是从 EpicLauncher 安装的 UE_4.21.2 版本。
为了方便测试,我创建了一个 VR Template 项目,注意 Target 选为Mobile

  1. 创建完成启动 UE Editor 之后,先检查 Oculus VR 插件有没有启用。

  2. 打开 Project Setting-Engin-Input 确认 Mobile-Default Touch Interfacenone

  3. 打开 Project Setting-Engine-Rending 确认 Mobile HDR 是关闭的:

  4. 打开 Project Setting-Platform-Android SDK

    这里主要是设置 Android 开发环境的路径,如 SDK/NDK/JDK,可以看上图的提示,如果不指定就会从系统中查找指定 NAME 的环境变量,因为我们安装 CodeWorks 时它已经帮我们添加了,此处可以忽略,如果是自己安装的环境则需要在这里指定或者直接添加到系统的环境变量。

  5. 打开 Priject Setting-Platform-Android

    分别执行 Configure NowAccept SDK License

    注意:如果不执行 Accept SDK License 会在 Launch 时遇到 License not accepted 错误。

1
License not accepted. SDK License must be accepted in the Android project settings to deploy your app to the device.  

Minimum SDK Version 设置为 25Target SDK Version 设置为26
以及启用 Enable FullScreen Immersive on KitKat and above device

之后向下滚动找到Advanced APK Packing,并启用下列两项:

  • Configure the AndroidManifest for deployment to Oculus Mobile
  • Rmove Oculus Signature File From Distribution APK

  1. 打开 Priject Setting-Project-Supported Platforms 启用Android
  2. 将该 VR 模板项目的 Editor Startup Map 以及 Game Default Map 改为MotionControllerMap
  3. 使用 Type-C 的 USB 线将 Quest 连接至 PC;
  4. 关闭 Editor,重启项目;

重启项目之后即可在编辑器工具栏的 Launch 下找到 Quest

启动即可开始编译并在 Quest 中测试当前项目 (会被自动安装到 Quest 中,在Unknow Source 下)。

如果在 Launch 或者打包时遇到 :app:assembleDebug 错误,则将 Project Setting-Platform-Android 下的 Enable Grade instead of Ant 关闭即可;如果非要用 grade 则检查 JAVE_HOME 环境变量设置的路径中是否含有特殊字符,最好只有数字 + 字母。

如果想要使用 Vulkan,则需要将 UE 升级到 4.22.2+.

解决 Quest 只追踪一只手柄

在第一次启动之后测试发现,在游戏中同一时刻只有一只手柄可以被控制,无法同时使用两只手柄。本来以为是 bug,查了一些资料后发现,这是因为没有在打包时强制指定要求 6DoF,详见文档:Enabling 6DoF head tracking
项目中默认的是没有指定 3DoF 还是 6DoF,默认是3DoF 的,所以只有一只手柄能够追踪。

解决的办法是在 Intermediate/Android/APK/AndroidManifest.xml 中添添加:

1
2
3
4
<uses-feature
android:name="android.hardware.vr.headtracking"
android:version="1"
android:required="true" />

但是,因为 AndroidManifest.xml 是被生成出来的,我们直接修改文件也会被覆盖,所以应该添加到项目的设置中:

看介绍,也可以编辑在 Build/Android/ManifestRequirementsOverride.txt(若不存在,手动创建),它会 替换 生成的 AndroidManifest.xml 中的 Requirements 部分,所以需要把已经生成的 AndroidManifest.xmlRequirements部分加上强制启用 6DoFuser-feature保存到 Build/Android/ManifestRequirementsOverride.txt 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Requirements -->
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="25" />
<uses-feature android:name="android.hardware.vr.headtracking" android:version="1" android:required="true" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- Supported texture compression formats (cooked) -->
<supports-gl-texture android:name="GL_KHR_texture_compression_astc_ldr" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.usb.host" />

重新运行或打包即可。

Package and Install

在 UE 中打包 Android 程序时有这个几个不同的可选 Texture 格式,目的是针对不同的硬件使用不同格式的压缩纹理:

Not all Android devices are made the same. In particular, there are 4 different kinds of rendering hardware. They each support different formats of compressed textures.

Format Description
ETC1 Supported by all Android based devices but cannot compress alpha textures (they are stored uncompressed). Recommend using an RGB and a separate alpha texture if need alpha to get better compression.
ETC2 Supported by all OpenGL 3.x class devices and supports alpha compression.
ATC Supported by Qualcomm Adreno GPUs and supports alpha compression.
DXT Supported by Nvidia Tegra GPUs and supports alpha compression.
PVRTC supported by PowerVR GPUs and supports alpha compression.
ASTC Latest Texture compression format allowing more quality control by specifying block size and supports alpha compression. Available on some devices at this point and will be required for Vulkan Level 1.

UE 文档的详细介绍:Android Development Reference

因为 Quest 使用的是 高通骁龙 835,所以打包时选择 ATC 即可。

打包完成之后在 Binaries/Android 目录下会有如下文件:

1
2
3
4
5
6
+---Binaries
| \---Android
| Install_QuestTemp-armv7-es2.bat
| main.1.com.imzlp.QuestTemp.obb
| QuestTemp-armv7-es2.apk
| Uninstall_QuestTemp-armv7-es2.bat

其中的 apkobb就是我们打包出来的游戏程序和数据包。
可以使用 adb 命令来安装 apk 和拷贝 obb 数据包。

1
2
3
adb install QuestTemp-armv7-es2.apk
20002 KB/s (47579881 bytes in 2.322s)
Success

然后将数据包拷贝到 /sdcard/Android/obb/com.imzlp.QuestTemp/ 目录下:

1
adb push main.1.com.imzlp.QuestTemp.obb /sdcard/Android/obb/com.imzlp.QuestTemp/main.1.com.imzlp.QuestTemp.obb

当然直接执行 Install_QuestTemp-armv7-es2.bat 也是可以的,它里面也是执行了这么几条指令。

VR Template Video

我简单录了一个这个 VR Template 在 Quest 上运行的一个视频:Oculus Quest Template,可以看到定位还是十分稳定的。

PC View

不同于 PC VR 在测试时可以直接把 VR 视角显示在 PC 显示器上,Quest 的视角想要在 PC 上实时预览 Quest 的视角除了投屏之外可以使用 scrcpy 这个开源工具,它是专门用于显示和控制 Android 设备的,我经常用它来控制手机,需要使用数据线连接设备,使用 adb 连接。scrcpy在对 Quest 使用时显示的是双眼视角:

Debug and Performance

那么我们怎么查看在 Quest 运行的程序的参数呢,比如帧率、机器的负载情况。
可以使用 Android 的 Debug 工具 adblogcat命令来查看:Android Debugging,以及 logcat 支持的参数:Logcat 命令行工具

首先,将 Quest 使用 USB 连接 PC,启动游戏,然后在 cmd 的窗口中输入以下命令:

1
adb logcat -s VrApi

之后就会看到一堆的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
C:\Users\visionsmile>adb logcat -s VrApi
--------- beginning of system
--------- beginning of main
09-28 23:00:12.250 18371 18386 D VrApi : targetSDKVersion 25
09-28 23:00:12.256 18371 18386 I VrApi : DEVICE MODEL NUMBER = Quest
09-28 23:00:12.256 18371 18386 I VrApi : DEVICE HARDWARE = monterey
09-28 23:00:12.256 18371 18386 I VrApi : DEVICE BUILD NAME = user-358570.9320.0
09-28 23:00:12.256 18371 18386 I VrApi : DEVICE BUILD TYPE = user
09-28 23:00:12.256 18371 18386 I VrApi : DEVICE OS VERSION = 7.1.1
09-28 23:00:12.256 18371 18386 I VrApi : VRAPI VERSION = 1.1.25.0-171385671-171385671 Sep 7 2019 04:35:06 Development RELEASE
09-28 23:00:12.256 18371 18386 I VrApi : VRAPI LOADER VERSION = 1.1.16.0
09-28 23:00:12.256 18371 18386 I VrApi : VRDRIVER VERSION = 8.0.0.148.626
09-28 23:00:12.256 18371 18386 I VrApi : APP NAME = QuestTemp
09-28 23:00:12.256 18371 18386 I VrApi : APP VERSION = 1.0 versionCode 1 internalVersionName <none>
09-28 23:00:12.256 18371 18386 I VrApi : APP VR TYPE = vr_only
09-28 23:00:12.256 18371 18386 I VrApi : APP PACKAGE NAME = com.imzlp.QuestTemp
09-28 23:00:12.256 18371 18386 I VrApi : APP ACTIVITY CLASS = com.epicgames.ue4.GameActivity
09-28 23:00:12.256 18371 18386 I VrApi : ovrModeParms::VRAPI_MODE_FLAG_ALLOW_POWER_SAVE = 1
09-28 23:00:12.256 18371 18386 I VrApi : ovrModeParms::VRAPI_MODE_FLAG_RESET_WINDOW_FULLSCREEN = 1
09-28 23:00:12.256 18371 18386 I VrApi : ovrModeParms::VRAPI_MODE_FLAG_NATIVE_WINDOW = 1
09-28 23:00:12.256 18371 18386 I VrApi : ovrModeParms::VRAPI_MODE_FLAG_FRONT_BUFFER_PROTECTED = 0
09-28 23:00:12.256 18371 18386 I VrApi : ovrModeParms::VRAPI_MODE_FLAG_FRONT_BUFFER_565 = 0
09-28 23:00:12.256 18371 18386 I VrApi : ovrModeParms::VRAPI_MODE_FLAG_FRONT_BUFFER_SRGB = 0
09-28 23:00:12.256 18371 18386 I VrApi : ovrModeParms::VRAPI_MODE_FLAG_CREATE_CONTEXT_NO_ERROR = 0
09-28 23:00:12.256 18371 18386 I VrApi : HMD sensor attached.
09-28 23:00:12.264 18371 18386 I VrApi : Client side frame sync enabled
09-28 23:00:12.276 18371 18386 I VrApi : OVR::Stats thread started
09-28 23:00:12.287 18371 18386 I VrApi : System brightness = 255
09-28 23:00:12.295 18371 18519 D VrApi : targetSDKVersion 25
09-28 23:00:12.300 18371 18386 I VrApi : Set brightness to 255
09-28 23:00:12.303 18371 18386 I VrApi : Set DND mode to true
09-28 23:00:12.329 18371 18386 I VrApi : System DND mode = true
09-28 23:00:12.329 18371 18386 I VrApi : ---------- vrapi_EnterVrMode [end] ----------
09-28 23:00:12.365 18371 18520 I VrApi : ovr_HandleHmdEvents: HMT was mounted
09-28 23:00:13.532 18371 18519 I VrApi : FPS=23,Prd=47ms,Tear=0,Early=0,Stale=1,VSnc=1,Lat=1,Fov=0,CPU4/GPU=3/3,1958/515MHz,OC=FF,TA=0/E0/E0,SP=N/F/F,Mem=1804MHz,Free=1588MB,PSM=0,PLS=0,Temp=28.0C/0.0C,TW=3.40ms,App=10.26ms,GD=3.30ms,CPU&GPU=9.47ms,LCnt=1,GPU%=0.62,CPU%=0.29(W0.33)
09-28 23:00:14.341 18371 18519 I VrApi : FPS=72,Prd=45ms,Tear=1,Early=62,Stale=10,VSnc=1,Lat=1,Fov=0,CPU4/GPU=3/3,1958/515MHz,OC=FF,TA=0/E0/E0,SP=N/F/F,Mem=1804MHz,Free=1592MB,PSM=0,PLS=0,Temp=28.0C/0.0C,TW=3.75ms,App=8.58ms,GD=0.00ms,CPU&GPU=9.06ms,LCnt=1,GPU%=0.76,CPU%=0.27(W0.32)
09-28 23:00:15.341 18371 18519 I VrApi : FPS=72,Prd=45ms,Tear=0,Early=69,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/3,1651/515MHz,OC=FF,TA=0/E0/E0,SP=N/F/F,Mem=1804MHz,Free=1593MB,PSM=0,PLS=0,Temp=28.0C/0.0C,TW=2.96ms,App=9.17ms,GD=0.65ms,CPU&GPU=9.24ms,LCnt=1,GPU%=0.77,CPU%=0.27(W0.33)

可以看到 VrApi 输出的内容:

1
FPS=72,Prd=45ms,Tear=0,Early=69,Stale=0,VSnc=1,Lat=1,Fov=0,CPU4/GPU=2/3,1651/515MHz,OC=FF,TA=0/E0/E0,SP=N/F/F,Mem=1804MHz,Free=1593MB,PSM=0,PLS=0,Temp=28.0C/0.0C,TW=2.96ms,App=9.17ms,GD=0.65ms,CPU&GPU=9.24ms,LCnt=1,GPU%=0.77,CPU%=0.27(W0.33)

这是当前 Quest 中的跑游戏时设备的各种参数。
这些参数的含义可以从 Oculus Quest 的文档中查看:

Capture video for Quest

Oculus 的 CTO 是游戏开发界的传奇人物——约翰·卡马克 (john camark),他近期(2019.10.18) 在 twitter 提到了在 Go/Quest 中满帧录屏的一个方法:

twitter link:If you want to capture full 60/72 fps video on Go/Quest instead of half rate, you can ‘adb shell setprop debug.oculus.fullRateCapture 1’.

即使用 adb 命令:

1
$ adb shell setprop debug.oculus.fullRateCapture 1

Documents and Tutorial

Oculus Quest 的开发文档:

UE 的 Android 开发文档:

视频教程:

Assets Download

本文内所有的资源:

Other

Not-PC based VR: