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 NAMEValue
ANDROID_HOMEC:\NVPACK\android-sdk-windows
ANDROID_NDK_ROOTC:\NVPACK\android-ndk-r14b
ANT_HOMEC:\NVPACK\apache-ant-1.8.2
GRADLE_HOMEC:\NVPACK\gradle-4.1
JAVA_HOMEC:\NVPACK\jdk1.8.0_77
NDK_ROOTC:\NVPACK\android-ndk-r14b
NDKROOTC:\NVPACK\android-ndk-r14b
NVPACK_NDK_TOOL_VERSION4.9
NVPACK_NDK_VERSIONandroid-ndk-r14b
NVPACK_ROOTD:\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/AndroidManifestRequirementsOverride.txt(若不存在,手动创建),它会替换生成的AndroidManifest.xml中的Requirements部分,所以需要把已经生成的AndroidManifest.xmlRequirements部分加上强制启用6DoFuser-feature保存到AndroidManifestRequirementsOverride.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.

FormatDescription
ETC1Supported 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.
ETC2Supported by all OpenGL 3.x class devices and supports alpha compression.
ATCSupported by Qualcomm Adreno GPUs and supports alpha compression.
DXTSupported by Nvidia Tegra GPUs and supports alpha compression.
PVRTCsupported by PowerVR GPUs and supports alpha compression.
ASTCLatest 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:

全文完,若有不足之处请评论指正。

扫描二维码,分享此文章

本文标题:Oculus Quest Development with UE4
文章作者:ZhaLiPeng
发布时间:2019年09月24日 10时41分
更新时间:2019年09月25日 01时07分
本文字数:本文一共有3.7k字
原始链接:https://imzlp.me/posts/30042/
许可协议: CC BY-NC-SA 4.0
转载请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!