虚拟现实XR(Virtual Reality,VR)技术分析
虚拟现实XR(Virtual Reality,VR)技术分析
1 XR技术综述
桌面虚拟现实(Virtual Reality,VR)历来是消费级3D计算机图形的主要显示技术。近来,立体视觉和头戴式显示器等更复杂的技术已变得更加普及。然而,大多数3D软件仍然仅设计用于支持桌面VR,并且必须进行修改以在技术上支持这些显示器并遵循其使用的最佳实践。需要评估现代3D游戏/图形引擎,并确定了它们在多大程度上适应不同类型的负担得起的VR显示器的输出,表明立体视觉得到了广泛的支持,无论是原生还是通过现有的适应。其它VR技术,如头戴式显示器、头部耦合透视(以及随之而来的鱼缸VR)很少得到原生支持。
2013年虚拟现实显示技术有桌面VR(串流)、立体视觉、头部耦合透视、头戴式显示器等几种,它们在模拟模型和用户感知方面的差异如下图:
立体视觉(Stereoscopy)是适用于双目视觉的桌面VR范式的扩展。立体镜通过两次渲染场景来实现这一点,每只眼睛一次,然后以这样的方式对图像进行编码和过滤,使每张图像只能被用户的一只眼睛看到。这种过滤最容易通过特殊的眼镜实现,眼镜的镜片设计为选择性地通过匹配显示器产生的两种编码之一。当前的编码方法是通过色谱、偏振、时间或空间。这些编码方法经常被分类为被动、主动或自动立体。被动和主动编码之间的区别取决于眼镜是否是电主动的:因此被动编码系统是颜色和极化,而唯一的主动编码是时间。自动立体显示器是不需要眼镜的显示器,因为它们在空间上进行编码,这意味着眼睛之间的物理距离足以过滤图像。
消费者立体显示器与计算机的接口方式与桌面VR显示器相同(通过VGA或DVI等视频接口)。由于这些接口中的大多数都没有特殊的立体观察模式,因此将两个立体图像以显示硬件可识别的格式打包成一个图像。此类帧封装格式包括交错、上下、并排、2D+深度和交错。由于这些标准化接口是软件将渲染图像传递给显示硬件的方式,因此软件应用程序不需要了解或适应编码系统的显示硬件。相反,图形引擎支持立体透视所需要的只是它能够从不同的虚拟相机位置渲染两个具有相同模拟状态的图像,并将它们组合成显示器支持的帧封装格式。
头部耦合透视(Head-coupled perspective,HCP) 的工作原理与桌面VR和立体视觉略有不同, 定义了一个虚拟窗口而不是虚拟相机,其边界是虚拟的窗口映射到用户显示器的边缘。因此,显示器上的图像取决于用户头部的相对位置,因为来自虚拟环境的对象会沿用户眼睛的方向投影到显示器上。这种投影可以使用桌面VR中使用的投影数学的离轴版本来完成。
为了做到这一点,必须实时准确地跟踪用户头部相对于显示器的位置。用于此目的的跟踪系统包括电枢、电磁/超声波跟踪器和图像- 基于跟踪。HCP的一个限制是,由于显示的图像取决于用户的位置,因此任何其他观看同一显示器的用户将感知到失真的图像,因为他们不会从正确的位置观看。
头戴式显示器(Head-mounted display,HMD)是另一种单用户VR技术,将立体视觉的增强功能与类似于HCP的大视场和头部耦合相结合。HMD背后的感知模型是完全覆盖用户眼睛的视觉输入,并将其替换为虚拟环境的包含视图。通过将一个或两个小型显示器安装在非常靠近用户眼前的镜头系统来实现的,以实现更自然的聚焦。由于显示器非常靠近用户的眼睛,显示器的任何部分只有一只眼睛可见,使系统具有自动立体感。
头饰中还嵌入了一个方向跟踪器,允许跟踪用户头部的旋转,允许用户通过将虚拟相机的方向绑定到用户头部的方向来使用自然的头部运动来环顾虚拟环境。它与HCP不同,HCP跟踪的是位置,而不是方向。支持HMD的软件要求与立体观察相同,但附加要求是图形引擎必须考虑HMD的方向,以及要校正的镜头系统引起的任何失真。
通过确定可以使用哪些扩展机制来实现所需的VR显示技术来衡量支持级别,已经结合了差异可以忽略不计的扩展机制(例如脚本和插件),并引入了两个额外的级别,不需要扩展(本机支持)和没有引擎内支持(重新设计)。扩展机制按引擎代码相对于实现VR支持的非引擎代码的比例排序,产生的支持级别及其排序如下:
5、原生支持。在原生支持VR技术的引擎中,引擎的开发人员特意编写了渲染管线,使用户只需最少的努力即可启用VR渲染。所需要做的就是检查开发人员工具中的选项或在引擎的脚本环境中设置变量。除了轻松启用该技术外,这些引擎还旨在避免常见的优化和快捷方式,这些优化和快捷方式在桌面VR显示器中并不明显,但随着更复杂的技术变得明显,一个常见的例子是渲染具有正确遮挡但深度不正确的对象,会导致立体镜下的深度提示冲突。
4、通过引擎内图形定制(包括节点图)。一些引擎的设计方式使得可以使用具有图形界面的自定义工具来更改渲染过程,一种方法是通过节点图,其中渲染管线的不同组件可以在多种配置中重新排列、修改和重新连接。根据支持的节点类型,有时可以配置节点以产生某些 VR 技术的效果。下图显示了虚幻引擎的材质编辑界面,该界面配置为将红青色立体立体渲染作为后处理效果。
3、通过引擎内编码(脚本或插件)。每个引擎都可以使用自定义代码进行扩展,使用定义明确但受限制的扩展点。两种常见的形式是在受限环境中运行的脚本,以及引擎加载并运行外部编译的代码插件,两种形式都可以访问引擎功能的子集,但是,插件也可以访问外部API,而脚本不能。由于通常实现特定于应用程序功能的机制,因此可用于自定义代码的引擎功能可能更多地针对人工智能、游戏逻辑和事件排序,而不是控制确切的渲染过程。
2、通过引擎源代码修改。除了免费的开源引擎,一些商业引擎通过适当的许可协议向用户提供其完整的源代码。通过访问完整的源代码,可以实现任何VR技术,尽管所需的修改量可能很大。
1、通过工程改造。对于不提供上述任何定制入口点的引擎,仍然可以通过重新设计进行一些更改。工程改造是逆向工程的一种形式,除了学习程序的一些工作原理之外,还修改了它的一些功能。对渲染管线进行完全逆向工程所需的工作量可能很大,因此更可取的是微创形式的再工程。其中一种方法是函数挂钩,即内部或库函数的调用被拦截并替换为自定义行为。由于很大一部分实时图形引擎使用OpenGL或Direct3D库进行硬件图形加速,因此这些库为通过函数挂钩实现纯视觉VR技术提供了可靠的入口点。事实证明,这种方法可以有效地将立体视觉添加到3D游戏。本文还展示了以这种方式实现头耦合透视也是可能的,通过挂钩加载投影矩阵(glFrustum和glLoadMatrix)的OpenGL函数,并用头部耦合矩阵替换原始程序提供的固定透视矩阵。
影响用户体验的因素有很多,虽然质量因素本质上与显示硬件相关,但适当的软件设计可以缓解这些问题,而粗心的设计可能会引入新问题。可以通过软件减轻的硬件质量因素的示例是串扰(立体)、A/C故障(立体)和跟踪延迟(HCP和HMD)。由于这些因素对于它们各自的显示技术来说是公认的,因此有众所周知的技术可以最大限度地减少它们引起的问题。解决方案分别是降低场景对比度、降低视差和最小化渲染延迟。
不正确的软件实现也会影响VR效果的质量,可能是由于粗心或桌面VR优化的结果。这方面的一个示例是任意位置的特殊图层(例如天空、阴影和第一人称玩家的身体)不同通道的深度。虽然在桌面VR中产生正确的遮挡,但在立体镜下添加双目视差提示会显示不正确的深度,并在这两个深度提示之间产生冲突。由于桌面VR的主导性质,这不是一个不常见的问题,并且可以作为另一个例子,说明简单的第三方实现可能不如原生VR支持。从这些方面应该注意到,虽然非原生VR实现可能满足必要的技术要求,但也必须考虑其它因素。
下表是2013年的主流引擎对VR的支持情况:
引擎 |
立体视觉 |
头部耦合透射 |
头戴式显示器 |
UDK |
4:图形定制。可以使用Unreal Kismet创建双摄像头装备,并使用材质编辑器打包输出。 |
1:工程改造。无法从引擎访问自定义相机投影,因此如果无源代码访问权限,则需要工程改造。 |
3:引擎编码。通过自定义实现立体化,可以通过自定义DLL获得头部方向并通过脚本绑定到相机。 |
Unity |
3:引擎编码。 |
3:引擎编码。 |
3:引擎编码。 |
CryENGINE |
5: 原生。 |
3:引擎编码。 |
3:引擎编码。 |
OGRE |
3:引擎编码。 |
3:引擎编码。 |
3:引擎编码。 |
虚拟现实中最重要的因素有:短余辉(Low Persistence)、延迟、现实。
VR的软件和硬件架构通常有好几层:C/C++接口、驱动程序DLL、VR服务层(在各应用之间分享和虚拟现实转换)等,下图是Oculus早期的架构图:
SDK的一般工作流程(以Oculus为例):
- ovrHmd_CreateDistortionMesh。通过UV来转换图像,比像素着色器的渲染效率更高,让Oculus能更灵活地修改失真。
- ovrHmd_BeginFrame。
- ovrHmd_GetEyePoses。
- 基于EyeRenderPose(游戏场景渲染)的立体渲染。
- ovrHmd_EndFrame。
Oculus SDK易于集成,无需创建着色器和网格,通过设备/系统指针和眼睛纹理,支持OpenGL和D3D9/10/11,必须为下一帧重新申请渲染状态。好处:与今后的Oculus硬件和特性更好地兼容,减少显卡设置错误,支持低延迟驱动显示屏访问,例如前前缓冲区渲染等,支持自动覆盖:延迟的测试、摄像头指南、调试数据、透视、平台覆盖。支持Unreal Engine 3、Unreal Engine 4、Unity等主流游戏引擎使用SDK渲染。支持扩展模式:头戴设备显示为一个OS Display,应用程序必须将一个窗口置于Rift监视器上,图标和Windows在错误的位置,Windows合成器处理Present,通常有至少一帧延迟,如果未完成CPU和GPU同步,则有更多延迟。另外,它支持Direct To Rift的功能,即输出到Rift,显示未成为桌面的一部分。头戴设备未被操作系统看到,避免跳跃窗口和图标,将Rift垂直同步(v-sync)与OS合成器分离,避免额外的GPU缓冲,使延迟降到最低,使用ovrHmd_AttachToWindow,窗口交换链输出被导向Rift,希望直接模式成为较长期的解决方案。
VR开发需要注意的事项:
- 不要控制玩家的头部!
- 注意第一人称动作。
- 照片现实主义是没有必要的。
- 不要使用电影级的渲染效果!比如可变焦距、过滤器、镜头光斑、泛光、胶片颗粒、暗角、景深等。
立体渲染质量检查:
- 左右方向正确吗?
- 双眼中的元素相同吗?
- 两幅图像代表同一时间吗?
- 刻度正确吗?
- 深度一致吗?
- 避免快速深度变化了吗?
良好的虚拟现实引擎必须满足以下条件:
- 高质量的视觉效果。高质量视觉效果指没有任何东西会分散你的注意力,让你沉浸在游戏中,良好的着色效果(但不一定是真实照片),通常意味着良好的抗锯齿。为什么良好的抗锯齿至关重要?人类感知的本质意味着我们很容易被高频噪点分心,分心会降低存在感,使用立体渲染时,锯齿伪影可能会更严重,它们会导致视网膜竞争,良好的抗锯齿比原生分辨率更重要。抗锯齿方法有:边缘几何AA,通常硬件加速;图像空间AA,非常适合大多数渲染管线,如FXAA、MLAA、SMAA等;时间AA,使用再投影进行时间超采样。
- 一致的高帧速率。为什么一致的高帧速率至关重要?在虚拟现实中,低帧速率看起来和感觉都很糟糕,如果没有高帧率,测试就很困难。在整个开发过程中保持高帧率,缺少V-sync也更为明显,因此,请确保启用了V-sync。
在当前的引擎中,“通道”的概念被广泛接受,如反射渲染、阴影渲染、后处理等,每个通道都有不同的要求,每个通道都要找出瓶颈所在。CPU?DrawCall?状态设置?资源设置?GPU?顶点处理受限?几何处理受限?像素处理受限?
绘制调用、状态设置或资源设置时CPU受限?考虑如何使用几何着色器,可以减少绘制调用的总数,阴影级联渲染:drawCallCount/n,其中n是层叠的数量,立方体贴图渲染:drawCallCount/6。降低资源设置成本,它还有其它特性可以帮助将处理从CPU上移开。
几何渲染单元将一个图元流转换为另一个可能更大的图元流,在像素着色器之前发生,即在直接顶点像素绘制调用中的顶点着色器之后,如果启用了细分,则在Hull着色器之后。
几何体着色器功能,渲染目标索引/视口索引,用于单程立方体贴图渲染、阴影级联、S3D、GS实例,允许逐图元运行同一几何体着色器的多次执行,而无需再次运行上一个着色器阶段。
用于立体3D渲染的几何体着色器,一种使引擎立体3D兼容的简单方法,为每种材质添加一个GS(或调整已有材质的GS),如下所示:
[maxvertexcount(3)]
void main(
inout TriangleStream<GS_OUTPUT> triangleStream,
triangle GS_INPUT input[3])
{
for(uint i = 0; i < 3; ++i)
{
GS_OUTPUT output;
output.position = (input[i].worldPosition , g_ViewProjectionMatrix);
triangleStream.Append(output);
}
}
顶点/几何体受限?通过压缩属性来减少顶点大小,在着色器阶段之间打包所有属性,如果正在使用用于放大或细分管线的几何体着色器,这一点很重要。考虑使用延迟获取(late fetch)法:将顶点属性数据绑定为使用的着色器阶段中的缓冲区,高度依赖硬件,始终调试性能,看看是否有影响!减少在GPU周围移动的数据。
像素受限?降低像素着色器的复杂性,减少每帧着色的像素数,一个使用较小渲染目标的实验上采样与高质量视觉冲突,引入光晕、微光和视网膜冲突。
考虑使用重新投影来加速立体3D渲染的方面,在PlayStation 3立体声3D游戏中获得巨大成功,然而,它只能在视差较小的情况下成功使用。
- 出色的跟踪和标定。一般由SDK处理跟踪,使用SDK提供的跟踪矩阵。游戏定义的默认观看位置和方向:
追踪玩家头部与摄像机的偏移量:
玩家眼睛相对于头部矩阵的偏移量:
跟踪器重置功能:设置头部位置,使其与游戏摄像机的位置和偏航对齐。重置位置和方向跟踪,重新调整游戏世界与现实世界的关系,以便固定的玩家位置,传递和游戏(Pass-and-play),匹配不同身高的玩家。
跟踪允许用户接近跟踪体积中的任何内容,无法实现超昂贵的效果,并声称“这只是角落里的一个小东西”,即使是最低画质也需要比传统创作的更高的逼真度,如果在跟踪体积中,必须是高保真的。
- 低延迟。为什么减少延迟如此重要?延迟是输入和响应之间的时间间隔,重要的不仅仅是始终如一的高帧率。不仅用于虚拟现实头部跟踪,提高响应能力在游戏中至关重要,游戏编程人员了解响应控制的必要性,网络程序员了解对响应性对手的需求等等。
假设现在拥有一个非常高效、高帧率、低延迟、超高质量的下一代引擎中拥有了出色的跟踪功能,该引擎针对虚拟现实进行了优化……引擎的工作完成了吗?当然不是!还有特定于平台的优化、跟踪外围设备、社交方面、游戏性/设计元素等工作。
Valve公司早在2014年就有多年的VR研究经验,联合了硬件和软件工程师,专为VR设计的定制化光学元件,显示技术——低持久性、全局显示,跟踪系统(基于基准的位置跟踪、基于点的桌面跟踪和控制器、激光跟踪HMD和控制器),SteamVR API–跨平台、OpenVR。
HTC Vive开发者版规格:刷新率是90赫兹(每帧11.11毫秒),低持久性,全局显示,帧缓冲区的分辨率是2160x1200(每只眼睛1080x1200),离屏渲染的宽高约1.4倍:每只眼睛1512x1680 = 254万个着色像素(蛮力),FOV约为110度,360⁰ 房间尺度跟踪,多个跟踪控制器和其它输入设备。
每秒着色可见像素数的估算:30赫兹时720p:2700万像素/秒,60Hz时1080p:1.24亿像素/秒,30英寸监视器2560x1600@60赫兹:2.45亿像素/秒,4k监视器4096x2160@30赫兹:2.65亿像素/秒,90赫兹时的VR 1512x1680x2:4.57亿像素/秒,可以将其降低到3.78亿像素/秒,相当于非虚拟现实渲染器在100赫兹时的30英寸监视器。
最低化GPU最低规格,最低规格越低,客户就越多,客户不应注意到锯齿,客户将锯齿称为“闪烁”,算法应该扩展到多个GPU上。
桌面VR可以尝试立体渲染(多GPU),AMD和NVIDIA都提供DX11扩展以加速跨多个GPU的立体渲染,AMD实现的帧速率几乎翻了一番,但还没有测试NVIDIA的实现。非常适合开发人员,团队中的每个人都可以在他们的开发盒中使用多GPU解决方案,在没有不舒服的低帧率的情况下打破帧率。
VR的交互技术包含选择、操纵、导航、系统控制等方面。三维选择包含从集合中拾取一个或多个对象、现实世界的隐喻(触摸/抓取、定点)、“自然”技术(简单虚拟手、射线投射)等。3D选择的影响因素有:技术(跟踪抖动、精度、延迟)、人类(手抖动)、环境(距离、遮挡)等,半天然的“天然”技术,即使是完全自然的技术也不是最佳的。可以使用双气泡(Double Bubble):扩展光线投射,动态体积光标、渐进式优化。3D选择技术的应用场景如下表:
相比Ray Cast,Double Bubble在选择时间、误差方面表现更好:
使用真实世界的隐喻,技术和现实世界的限制,打破现实世界的假设:
每种操作方式在各个阶段的描述如下:
3D交互的最后的想法是自然主义vs 魔法(超自然、超级自然)、与不精确工具的精确交互(渐进式优化、动态C/D增益、虚拟摩擦力):
虚拟实体的影响也比较关键,许多关于化身影响的研究,对社交互动至关重要,对于存在(对某些人)也至关重要。
在手势和身体方面,需要手势向他人解释,有些人经常做手势,语言学研究人员研究了手势对解释困难概念能力的影响。
从左到右:无化身(avatar)、有化身但无移动、完整的化身和移动。
拥有化身显著提高了执行对象记忆任务的能力,有化身的人比没有化身的人做更多的手势。延迟至关重要,较低的延迟倾向于更“自然”的接口,但在所有情况下,这些接口可能不是最有效的接口。虚拟身体对某些用户非常重要,“虚拟现实”研究可以在众多学科中找到,因为其影响和需求非常广泛,一个非常多样化的研究社区促成了令人兴奋和有趣的研究合作。
在现实物理空间和虚拟现实的空间映射中,虚拟现实的物理定律是可变的,人类的感知是可塑的,我们可以利用它来提高可用性,可以创造超现实、神奇的体验。
XR通常存在空间感知技术,运动跟踪-深度感应-区域学习,表面重建–平面和孔洞检测。
空间感知的相关设备:
对于Microsoft的HoloLens,采用了红外相机空间映射:
支持运动和手势跟踪:
语音识别,包含系统级命令、用户可配置命令。
空间处理HoloToolkit支持基本的空间映射(访问/可视化空间数据,保存/加载房间)和空间处理(曲面网格到平面,墙、天花板、地板、桌子,未知,地板缓冲器,天花板缓冲器,自定义形状定义)。
Tango运动追踪支持视觉惯性里程计(VIO,跟踪图像差异,惯性运动传感器,组合以提高精度),限制是漂移、无内存、照明等。2016年的Tango和HoloLens的对比如下:
2017年的VR游戏Climb采用了严密的计划,成功解决了新平台问题,运动方面取得突破,使用保守的技术方法,设计驱动的功能有时会出现问题。
Robinson分析性能和内存,平台工具运行良好,艺术团队成功采用程序可视化分析工具,在屏幕上的OOM崩溃跟踪内存,新功能可分析当天保存的峰值。
注释点(透镜匹配)渲染上,利用PS4近/宽渲染支持,对于每只眼睛,渲染内部和外部视图。
大大有助于在性能和分辨率之间找到最佳点,PS4渲染的内部面积等于1.5倍渲染比例(1620p),PS4 Pro将其增加到1.9倍,更大的内径
外环在PS4上采样不足,Pro 1:1,需要渲染场景四次。在渲染线程上录制场景drawcalls的成本高出四倍,为场景和照明重新提交相同的命令缓冲区:
后处理仍录制4次,有些数据需要修补,每次提交后覆盖现有的每视图常量缓冲区在每次提交后复制后处理(对象速度)期间所需的渲染目标。为了节省GPU成本,顶点着色器执行了4次,但开销可以接受,通过将Post交错作为异步作业来吸收GBuffer中的顶点开销,填充HTILE掩码以拒绝相关区域之外的像素。
总之,Robinson的计划/时间表不稳定,预留空间给开发方(性能、内容、游戏性),移动和用户选项的结果参差不齐,技术创新高度成功,媒体/平台上凸起的可视栏。人工移动已经存在并将继续存在,用户界面/用户体验还有很长的路要走,VR性能并不难,峰值可以
在主流硬件上实现高保真,到目前为止,只是触及表面。下图是VR系统场景的组件:
输入处理器、模拟处理器、渲染处理器和世界数据库关系如下:
VR分类可以基于两个因素:使用的技术类型和精神沉浸程度,具体如下图:
解决未来关键的XR技术挑战包含显示、照明、运动追踪、电量和散热、连接等。
1 软件架构
VR应用常涉及实现所有事情!如硬件故障,需要支持一切(太古代),从SDK提取输入,管理SDK,UI框架等。其中的一种VR分层架构如下:
- 特定于SDK的输入类:每个硬件/SDK一个类,没有特定于项目的逻辑!监听设备输入,调用抽象处理程序,调用工具的down/hold/up,调用常规输入的down/hold/up。
- 输入组件:SDK特定组件类包含对硬件功能的特定引用:
- 所有SDK都以Libs/dir的形式存在于项目中:
• public class ViveControllerComponents : WandComponents
• {
• public SteamVR_Controller.Device viveController;
• }
特定于类别的组件类包含硬件类型的公共属性:
public class WandComponents : InputComponents
{
public Transform handTrans;
public override Vector3 Position { get { return handTrans.position; } }
public override Vector3 Forward { get { return handTrans.forward; } }
public override Quaternion Rotation {get{ return handTrans.rotation; }}
}
InputComponents基类包含大多数抽象数据:
public class InputComponents
{
public virtual bool Valid { get { return true; } }
public virtual Vector3 Position { get { return Vector3.zero; } }
public virtual Vector3 Forward { get { return Vector3.forward; } }
public virtual Quaternion Rotation { get { return Quaternion.identity; } }
}
• 工具基类。
• // 每个SDK的向下/保持/向上挂钩
• public virtual void DoToolDown_Sixense(SixenseComponents sxComponents)
• {
• DoToolDown_Wand(sxComponents);
• }
• public virtual void DoToolDown_Leap(LeapComponents leapComponents)
• {
• DoToolDown_Optical(leapComponents);
• }
• public virtual void DoToolDown_Tango(TangoComponents tangoComponents) {
• DoToolDown_PointCloud(tangoComponents);
• }
•
• // 每个类别的向下/保持/向上挂钩
• public virtual void DoToolDown_Wand(WandComponents wandComponents)
• {
• DoToolDown_Core(wandComponents);
• }
• public virtual void DoToolDown_Optical(OpticalComponents opticalComps)
• {
• DoToolDown_Core(opticalComps);
• }
• public virtual void DoToolDown_PointCloud(PCComponents pcComponents)
• {
• DoToolDown_Core(pcComponents);
• }
•
• // 工具基础函数
• DoToolDown_Core
• DoToolDownAndHit*
• DoToolHeld_Core
• DoToolUp_Core
• DoToolDisplay_Core
•
• public virtual void DoToolDown_Core(InputComponents comp)
• {
• if (Physics.Raycast(comp.Position, comp.Forward, out hit, dist, layers))
• {
• DoToolDownAndHit(comp);
• }
• }
• 特定工具类型。
• // 可以覆盖DoToolDown_Core等,实现完全平台无关逻辑
• public class MoveTool : Tool
• {
• protected override void DoToolHeldAndHit(InputComps comps)
• {
• selectedTrans.position = hit.point;
• }
• }
•
• // 可以覆盖任何类别或特定于SDK的挂钩,以实现更定制的行为
• public class MoveTool : Tool
• {
• // ...
• protected override void DoToolHeld_Optical(OpticalComps comps)
• {
• // Move mechanic that’s more appropriate for optical control
• }
• }
• 硬件输入基类。非工具抽象输入,适用于一般游戏功能和一次性交互,三种处理方法:
• // 比如Unity的原生输入类
• bool HardwareInput.ButtonADown/Held/Up
• // 当想要那个观察者的时
• event HardwareInput.OnButtonADown
• // 集中输入/游戏逻辑
• void HardwareInput.HandleButtonADown()
• 游戏性/一般输入类...。
• // 一次性输入
• public class GameplayController : MonoBehaviour
• {
• void Update()
• {
• if(HardwareInput.TriggerDown)
• {
• WorldConsole.Log("Fire ze missiles!");
• }
• }
•
• void Awake()
• {
• HardwareInput.OnButtonADown += HandleButtonA;
• }
•
• void HandleButtonA()
• {
• WorldConsole.Log("Boom!"); // Btw: use a “world console”!
• }
• }
•
• // 组件的一般输入, 更新的三种方法:
• // 添加硬件输入/位置/向前等
• HardwareInput.ButtonADown/Held/Up
• // 传递包含组件的事件参数
• HardwareInput.OnButtonADown(args)
• HandleButtonADown(components)
SDK太多了!SDK之间的AndroidManifest和插件冲突,在某些情况下,可以通过合并清单来解决(例如Cardboard+Nod),在许多情况下,只需要将冲突的SDK移入或移出Asset文件夹,可以连接到构建管线中。对于多SDK场景设置,将场景设置为支持所有SDK,Player对象包含用于ViveInput、GamepadInput、CardboardInput、NodeInput、LeapInput、TangoInput的组件...好处是场景之间没有重复的工作,所有设备都可以同时启用(例如Vive+Leap)。好处是让多种设备类型交互意味着新的设计挑战,更多平台==更复杂的场景,可以将播放器拆分为更易于管理的预置体,并在运行时或使用编辑器脚本组装这些预置。对于SDK管理器编辑器脚本,在编辑器中或在构建时启用/禁用每个平台的组件和对象。
public void SetupForCardboard()
{
Setup(
// Build settings
bundleIdentifier: "io.archean.cardboard",
vrSupported: false,
// GameObjects
cameraMasterActive: true,
sixenseContainerActive: false,
// MonoBehaviours
cardboardInputEnabled: true
);
}
此外,可以自定义输入模块将允许向uGUI添加新的硬件支持。块状和点击用户界面(Block-and-pointer UI),点击或按下时,<设备>将光线投射输入用户界面(例如Vive),或简单的碰撞(如Leap)。自定义按钮组件:
ButtonHandler类中有一个巨大的switch语句来映射所有动作:
switch(button.action)
{
case ButtonStrings.Action_TogglePalette: TogglePalette(button, state); break;
case ButtonStrings.Action_ChangePage: ChangePage(button, state); break;
case ButtonStrings.Action_ChangePagination:ChangePagination(button); break;
case ButtonStrings.Action_SelectProp: SelectProp(button, state); break;
case ButtonStrings.Action_SelectTool: Tool.HandleSelectToolButton(button); break;
…
还有一个文件,里面有用于操作的常量字符串:
public const string Action_TogglePalette = "togglePalette";
public const string Action_ChangePage = "changePage";
public const string Action_ChangePagination = "changePagination";
public const string Action_SelectProp = "propSelect";
public const string Action_SelectTool = "toolSelect";
....
通过参数字段可以获得更高级和可重用的功能,用户界面代码集中,按钮可以传递任何数据类型,非常方便。所以你想做一个多平台的虚拟现实应用,SteamVR&Cardboard加入Unity的原生VR支持,从一开始就应该计划多平台。
虚拟现实系统由硬件和软件两个主要子系统组成,硬件可进一步分为计算机或VR引擎和I/O设备,而软件可分为应用软件和数据库,如下所示。
下图显示了名为CalVR的VR框架的不同模块,CalVR本身构建在OSG之上,而OSG又构建在OpenGL之上。菜单API目前支持两个菜单小部件库:Board菜单和Bubble菜单。CalVR使用一组设备驱动程序,例如Kinect或Ring鼠标,它允许运行自定义插件。
下图是VR系统的第三人称透视图。假设工程硬件和软件是完整的VR系统是错误的:有机体及其与硬件的交互同样重要。此外,在VR体验过程中,与周围物理世界的交互不断发生。
缓冲通常用于视觉渲染管线中,以避免撕裂和丢失帧;然而,它引入了更多的延迟,对VR不利。
2 Quest 2开发
Quest 2是Oculus于2020年发行的一款VR一体机,使用了高通Snapdragon XR2芯片组,其中Snapdragon XR2芯片组的硬件基本参数如下:
CPU |
Octa-core Kryo 585 (1 x 2.84 GHz, 3 x 2.42 GHz, 4 x 1.8 GHz) |
GPU |
Adreno 650 |
在Quest 2开发互动应用,一种可行的绘制调用预算是:每个网格/对象1个调用,该对象上的每个唯一材质(或材质实例)调用1次,限制网格上材质的数量,Atlas纹理可减少材质数量,可以合并网格的位置。
可以使用RenderDoc与任务的连接,捕获任务绘制的帧,需要参考draw调用的总数,单步执行单个绘制调用,发现性能方面的潜在问题。
OVRMetrics/FPS计数器:FPS是最重要的性能指标!理想情况下保持在72,但至少在65以上,使用FPS计数器查看运行时的帧速率。
使用前向渲染,无深度渲染,单通道立体渲染,消除锯齿,注视点渲染。
在UE和Unity中设置前向渲染。
在UE和Unity中设置单通道立体渲染。
Oculus Quest支持固定注视点渲染(Fixed Foveated Rendering,FFR)。FFR允许以低于眼睛缓冲区中心部分的分辨率渲染眼睛缓冲区的边缘。请注意,与其它形式的注视点技术不同,FFR不基于眼睛跟踪,高分辨率像素“固定”在眼睛缓冲区的中心。使用FFR的视觉效果几乎难以察觉,但FFR的性能优势包括:
- 显著提高GPU填充性能。
- 降低功耗,从而减少热量并延长电池寿命。
- 使应用程序能够提高眼睛纹理的分辨率,从而改善观看体验,同时保持性能和功耗水平。
使用FFR时有一些权衡:
- FFR对于低对比度纹理(包括背景图像和大型对象)最有用。
- FFR对于高对比度项目(如文本和精细详细的图像)不太有用,并且会导致图像质量明显下降。
- 复杂片段着色器受益于FFR。
可以逐帧调整FFR级别,以便在性能和视觉质量之间实现最佳权衡。通常,应该尽可能多地使用FFR,并将其设置为尽可能高的级别,但应该测试内容并查找任何不需要的视觉瑕疵。因为应该尽量使用FFR,所以建议使用动态FFR,根据GPU负载和应用程序的要求自动设置FFR级别。
FFR提供的增益(或损失)通常取决于应用程序的像素着色器成本。FFR可使像素密集型应用程序的性能提高25%。另一方面,使用非常简单的着色器(未绑定到GPU填充)的应用程序可能不会看到FFR的显著改进。高度ALU绑定的应用程序将从中受益,如下图所示,它在场景中收集GPU百分比。鉴于16%的GPU利用率来自timewarp(因此不受FFR的影响),此图显示的性能比低设置提高了6.5%,比中设置提高了11.5%,比高设置提高了21%。
这显示了使用FFR的最佳情况。如果在具有非常简单的像素着色器的应用程序上执行相同的测试,则实际上可能会在低设置上产生净损失,因为使用FFR的固定开销可能高于在相对较少的几个像素上的渲染节省。事实上,在这种情况下,可能会体验到高设置的轻微增益,但它不值得图像质量损失。与传统的2D屏幕不同,VR设备要求向观众显示的图像扭曲,以匹配HMD中镜头的曲率。这种扭曲使我们能够感知到一个更大的视野,而不仅仅是简单地看一个原始的显示器。下图显示了扭曲的效果,其中2D平面(水平线)扭曲成球形:
由于扭曲,构成眼睛纹理的像素的表示非常不均匀。在FOV边缘创建后扭曲区域需要比FOV中心更多的像素,这导致FOV边缘的像素密度高于中间的。由于用户通常会朝屏幕中央看,会产生很大的反作用。最重要的是,镜头会模糊视野的边缘,因此即使在眼睛纹理的这一部分渲染了许多像素,图像的清晰度也会丢失。GPU花费大量时间渲染FOV边缘无法清晰看到的像素,是非常低效的。
注视点渲染通过在计算期间降低输出图像的分辨率来回收一些浪费的GPU处理资源,它是通过控制GPU上各个渲染分片的分辨率来实现的。Oculus Quest使用分块(tile)渲染器,FFR的工作原理是控制各个分块的分辨率,并确保落在眼睛缓冲区边缘的分块的分辨率低于中心,从而减少了GPU需要填充的像素数量,而不会明显降低后扭曲(post-distortion)图像的质量,因此,对于渲染大量像素的应用程序,GPU性能有了非常显著的改善。
下面的屏幕截图显示了1024x1024眼缓冲区的分块分辨率倍增图。这些颜色表示以下示例图像中的以下分辨率级别,以演示FFR设置:
- 白色=全分辨率:这是FOV的中心,纹理的每个像素都由GPU独立计算。
- 红色=1/2分辨率:GPU仅计算一半像素。当GPU将其计算结果存储在通用内存中时,将在解析时从计算的像素中插值缺失的像素。
- 绿色=1/4分辨率:GPU仅计算四分之一的像素。当GPU将其计算结果存储在通用内存中时,将在解析时从计算的像素中插值缺失的像素。
- 蓝色=1/8分辨率:GPU仅计算八分之一的像素。当GPU将其计算结果存储在通用内存中时,将在解析时从计算的像素中插值缺失的像素。
- 粉红色=1/16分辨率:GPU仅计算十六分之一的像素。当GPU将其计算结果存储在通用内存中时,将在解析时从计算的像素中插值缺失的像素。
Quest支持动态注视点功能,可以配置注视点级别,通过启用动态注视点,根据GPU利用率自动调整。启用动态注视点时,注视点级别将自动调整,指定的注视点级别为最大值。根据GPU的利用率和应用程序的要求,系统会上升到所选的注视点级别,但决不会超过该级别。尽量使用动态FFR,而不是Unreal的动态分辨率功能。有多种方法可以设置FFR级别并启用动态注视点:
- 项目设置。可以在Unreal项目设置中的OculusVR插件页面设置FFR级别。
- API设置。可以使用以下方法将FFR级别设置为以下任何索引:
void UOculusFunctionLibrary::SetFixedFoveatedRenderingLevel(EFixedFoveatedRenderingLevel level, bool isDynamic)
- 蓝图设置。通过以下蓝图节点获取和设置FFR级别:
Multi-View是基于Android的Oculus平台的高级渲染功能。如果应用程序受到CPU的限制,强烈建议使用多视图来提高性能。在典型的立体渲染中,必须按顺序渲染每个眼睛缓冲区,从而使应用程序和驱动程序开销加倍。启用“多视图”后,对象将渲染一次到左眼缓冲区,然后自动复制到右眼缓冲区,并对顶点位置和视图相关变量(如反射)进行适当修改。OpenGL和Vulkan API支持多视图渲染。若要开启Multi-View,打开虚幻的设置页面:Edit > Project Settings > Engine > Rendering,勾选以下选项:
相位同步(Phase Sync)是一种用于自适应管理延迟的帧定时管理技术,它可作为UE4.23及更高版本中的一个选项用于Quest和Quest 2应用程序。Phase Sync为Oculus Quest和Quest 2应用程序提供了一种替代传统固定延迟模式的方法来管理帧计时。固定延迟模式意味着尽可能早地合成帧,以避免丢失当前帧和需要重用过时帧,过时帧会对用户体验产生负面影响。与固定延迟不同,相位同步根据应用程序的工作负载自适应地处理帧定时。相位同步的目标是在合成器需要完成的帧之前进行帧完成渲染,可以减少渲染延迟,而不会丢失帧。针对Quest和Quest 2的应用程序应启用相位同步提供的自适应帧定时。请注意,Quest 2的CPU和GPU资源比Quest多,并且可能会过早渲染帧,从而增加延迟,而相位同步有助于减少此延迟。下图显示了典型多线程VR应用程序的固定延迟与启用相位同步之间的差异。
启用相位同步时,请注意以下事项:
- 没有额外的性能开销。
- 如果应用程序的工作负载剧烈波动或频繁出现峰值,则相位同步可能会导致比未启用相位同步时使用更陈旧的帧。
- 延迟锁(Late-Latching)和相位同步通常是相辅相成的。
- 如果额外延迟模式和相位同步都已启用,则将忽略额外延迟模式。
要在虚幻引擎中启用相位同步,打开Edit > Project Settings > Plugins > OculusVR,在Mobile部分,选中Phase Sync复选框。
测试相位同步:在应用程序中启用阶段同步后,可以通过检查logcat日志来验证它是否处于活动状态,并查看它节省了多少延迟。
adb logcat -s VrApi
如果相位同步未激活,Lat值为Lat=0或Lat=1,表示额外延迟模式。如果相位同步处于活动状态,则Lat值为Lat=-1,表示延迟是动态管理的。
Prd值指示由运行时测量的渲染延迟。要计算相位同步节省了多少延迟,请比较相位同步处于活动状态和未处于活动状态时的Prd值。例如,如果有相位同步的Prd为35ms,没有相位同步的Prd为45ms,则使用相位同步可节省10ms的延迟。为了更容易地比较有无相位同步的性能,可以使用adb shell setprop打开和关闭相位同步。更改setprop后,必须重新启动应用程序,更改才能生效。
- 关闭:adb shell setprop debug.oculus.phaseSync 0.
- 打开:adb shell setprop debug.oculus.phaseSync 1.
可以在Quest上的Unreal Engine中使用某些色调映射效果,而不会产生与色调映射相关的传统性能成本。Oculus集成可以用最少600微秒的额外渲染时间渲染色调映射,因为它使用Vulkan subpass而不是Unreal Engine的移动HDR模式或额外的渲染通道。此功能仅在使用Vulkan的UE 4.26的Oculus分支中可用。具体详情可参阅Tone Mapping in Unreal Engine。
Quest在UE中支持VR合成器层(VR Compositor Layers)。使用Unreal,可以将透明或不透明的四边形、立方体贴图或圆柱形覆盖层添加到级别,作为合成器层。异步时间扭曲合成器层(例如世界锁定覆盖)以与合成器相同的帧速率渲染,而不是以应用程序帧速率渲染。它们不太容易抖动,并且通过镜头进行光线跟踪,从而提高了其上显示的纹理的清晰度。
建议对文本使用合成器层,在合成器层上渲染的文本更清晰。另外,凝视光标和UI很适合渲染为四边形合成器层。圆柱体对于平滑曲线UI界面可能很有用,立方体贴图可用于启动场景或Skybox。建议在加载场景中使用立方体贴图合成器层,这样即使应用程序不执行任何更新,它也将始终以稳定的最小帧速率显示,可以显著缩短应用程序启动时间。
在4.13及更高版本的Unreal中支持四边形、圆柱体和立方体贴图层。默认情况下,VR合成器层始终显示在场景中所有其它对象的顶部。可以通过启用“支持深度”(Supports depth),将合成器层设置为响应深度定位。如果使用多个图层,请使用优先级设置控制图层显示的深度顺序,较低的值表示优先级较高(例如,0在1之前)。请注意,启用Supports depth度可能会影响性能,因此请谨慎使用,并确保评估其影响。
要创建一个overlay,请执行以下操作:
- 创建一个Pawn并将其添加到关卡。可以使用UMG UI设计器向Pawn添加任何所需的UI元素。
- 选择Pawn,选择Add Component,然后选择Stereo Layer。
- 在“Stereo Layer options”下,将“Stereo Layer Type”设置为“Quad Layer”、“Cylinder Layer”或“Equirect Layer”。
- 将“Stereo Layer Type”设定为“Face Locked”、“Torso Locked”或“World Locked”。
- 在 Quad Stereo Layer Properties或Cylinder Stereo Layer Properties中以世界单位设置overlay尺寸。
- 选择“支持立体层中的深度”(Supports Depth in Stereo Layer)可将合成器层设置为不总是显示在其他场景几何体的顶部。请注意,此设置可能会影响性能。
- 根据需要配置纹理和其它属性。
- 选中“双三次过滤”复选框,启用为Quest显示调整的GPU硬件双三次过滤,以在呈现VR图像时享受额外的保真度。
- 注意:随着内核占用空间的增加,双三次过滤需要更多的GPU资源,对于三线性缩小尤其如此,因为它需要从单独的mip级别进行两次双三次计算。如果直接用于合成器层,增加的GPU成本将在合成计时中表现出来,可能会导致帧下降并对VR体验产生负面影响。应该权衡增加的视觉保真度与提供最佳VR用户体验所需的额外GPU资源。
将组件从属于的Pawn将固定在四边形或圆柱体的中心。最多可以将三个VR合成器层添加到移动应用程序,最多可以将十五个VR合成器层添加到Rift应用程序。
在光影方面,烘焙灯光以获得更高性能的灯光效果,但请记住,烘焙光照贴图需要时间,根据团队规模和环境数量平衡时间。一次仅一个动态灯光,无论如何,还是要考虑在静态区域烘焙。动态阴影非常昂贵,一次只能投射一个阴影灯光,仅硬阴影,尽可能避免阴影投射,除非游戏渲染非常轻量级。无照明(Unlit)着色器的性能非常好,消除花费在照明和烘焙光照贴图上的时间,总体上需要较少的纹理。照明,但使用卡通阴影作为替代方案,在不完全不照明的情况下计算更少。
保持低纹理分辨率,使用尽可能少的图集,也许可以尝试在没有特定贴图的情况下进行,或者打包到RGBA通道中,尽可能重复使用和平铺,别忘了mip!指令数影响性能,纹理的数量会影响性能,尤其是当它们在屏幕上平铺时,但是着色器也可以真正有助于创建美丽和独特的外观。尝试使用轻量级着色器,看看它们能做什么意外的工作。切换材质和着色器对每次绘制调用的性能有轻微影响,Atlas尽可能减少独特材质的数量,即使它不会减少绘制调用。如果可以,请合并着色器以限制唯一着色器的数量。
仅在内存中实例化不会减少绘制调用,某些类型的实例执行合并/批处理绘制调用,LOD仍然存在,并且仍然有效!某些类型的实例还使用LOD和批处理绘制调用。使用良好的行业惯例,尤其是在从高保真到低保真的情况下。探索新风格!将有用且适用的低多边形样式集成到游戏中,作为难题的解决方案,示例:如果在使用传统方法时遇到性能问题,请查看低多边形样式中如何处理树木或树叶。
可以使用批处理,在一次调用中绘制多个对象!不同引擎有不同的类型和方法,但谨记批处理都有一点开销。找出批量或使用其他解决方案(如合并)是否更便宜。在Unity中,有动态批处理(300顶点以下相同材质的相同网格,一些开销,但对于小的重复对象非常好)和静态批处理(更高的多边形模型,但使用更多内存)。在UE中有实例化静态网格(Instanced Static Meshes,一次draw调用,但在其他方面不会节省太多性能)和层次实例化静态网格(Hierarchical Instanced Static Meshes,LOD和裁剪可生效,但很难使用,所以需要制作一个工具来帮助使用者)。
对于半透明,具有透明度的小对象的性能非常好,重叠的大型透明对象对性能影响最大,尽量少使用透明度,在设备上充分测试半透明!!
左:软Alpha卡片效果多少存在一些问题;右:顶点雾是一种仍然有效的旧方法。
减少半透明的空白像素区域,可有效提升性能。
通过过渡获得创意!不是所有的东西都需要Fade。
对于后处理,颜色校正可以在着色器中完成,如果真的需要在一些地方bloom,可以用一些卡片来伪装它。可能无法使用景深、屏幕叠加(screen overlay)和奇特的后期着色器。思考需要从后处理中获得什么,并尝试以其它方式实现。
3 OpenXR
OpenXR是Kronos出的XR标准API,OpenXR提供跨平台、高性能的访问,可跨多个平台直接访问XR设备运行时。
典型的OpenXR应用程序的高级概述,包括函数调用顺序、对象创建、会话状态更改和渲染循环(下图)。
更多OpenXR的介绍参考官网:https://khronos.org/openxr。
2 光学和成像
所有镜头都会引入图像扭曲、色差和其它失真,我们需要在软件中尽可能地对其进行校正!通过HMD镜头看到的栅格,可发现图像的横向(xy)扭曲和色差:扭曲取决于波长!
图像扭曲(挤压变形)的两种形式:透镜畸变和筒体变形:
其中上图左是由光学部件(镜头)引起的,而上图右是应用程序为了抗光学畸变而有意为之。整体工作原理如下:
可调节的眼镜佩戴者无需调整即可适应瞳孔间距的变化:
下图则是关键的(上)和可容忍的(下)参数示意图:
对于立体3D而言,在摄影立体和头盔显示器的固定设置下的所需的焦距要求:
结合下图,(a)大多数HMD的视野都很窄,(b)实现宽视场需要更高分辨率的显示器,(c)或更大的像素。(d)如何做到两全其美?利用眼睛的可变敏锐度,(e)使用扭曲着色器压缩图像的边缘,(f)光学元件应用反向失真,使边缘看起来再次正确,(g)中心像素较小,边缘像素较大。
光学与变形:Warp通道分别为RGB使用3组UV,以考虑空间和颜色失真。
可视化1.4倍的渲染目标。其中上图是扭曲前,下图是扭曲后。
模板网格(隐藏区域网格):用模板屏蔽掉实际上无法透过镜头看到的像素,GPU在提前模板拒绝时速度很快。或者,可以渲染到接近z的深度缓冲区,以便所有像素都可启用提前z测试,透镜会产生径向对称变形,意味着可以有效地看到投影在面板上的圆形区域。
模板网格图例。从上到下从左到右依次是:扭曲视图、理想扭曲视图、浪费的空间、无扭曲视图、无扭曲视图(屏蔽无效像素)、最终无扭曲视图、最终无扭曲的分离视图。
模板网格(隐藏区域网格):SteamVR/OpenVR API提供此网格,填充率可以降低17%!无模板网格:VR 1512x1680x2@90Hz:4.57亿像素/秒,每只眼睛254万像素(总计508万像素),带模板网格:VR 1512x1680x2@90Hz:3.78亿像素/秒,每只眼睛约210万像素(总计420万像素)。
扭曲网格,依次是:镜头畸变网格、暴力、剔除0-1之外的UV、剔除模板网格、收缩扭曲。
VR还涉及透镜畸变和像差校正(aberration correction):
下图是Oculus Rift的透镜结构和原理:
人类视觉颜色系统如下图,人眼无法测量,大脑无法测量每个波长的光,相反,眼睛测量三个响应值=(S、M、L),根据S、M、L锥的响应函数。
人类的单目和双目视野如下图,每只眼睛约160°视野(总视野约200°,注:不考虑眼睛在眼窝中旋转的能力)。
具有人眼视力的VR显示器:
考虑有限的VR显示刷新率:
情况2:相对于眼睛移动的对象:
案例3:眼睛移动以跟踪移动对象:
提高帧速率可以减少抖动,较高的帧速率(下图最右侧的图表),更接近地面真相:
减少抖动:低持久性显示。低持久性显示:像素在小部分帧中发光,Oculus DK2 OLED低持久性显示器:75 Hz帧速率=每帧约13 ms,像素持久性=2-3毫秒。
3 延迟和滞后
VR中的延迟要求具有挑战性,VR图形系统的目标是实现“存在”,将大脑诱使成,认为它所看到的是真实的,实现存在需要极低延迟的系统。当你移动头时,你看到的必须改变!端到端延迟:从头部移动到新光子到达眼睛的时间。测量用户头部运动,更新场景/摄像机位置,渲染新图像,将图像传送至HMD,然后传送至HMD中的显示器,实际从显示器发出光(光子击中用户的眼睛)。VR的延迟目标:10-25 ms,需要极低延迟的头部跟踪,需要极低延迟的渲染和显示。
考虑1000 x 1000跨100°视野的显示器,每度10像素。假设在1秒内将头部移动90°(仅以中等速度),系统的端到端延迟为50毫秒(1/20秒),结果是显示的像素与理想系统中的像素相差4.5°~45像素,延迟为0。减少VR的延迟不仅对对抗负面影响(眩晕等)很重要,而且因为延迟对行动的执行至关重要,据称18ms的延迟很难察觉,但它仍然会影响性能,如果要优化3D交互,则必须分析延迟,否则结果可能无法传输。挑战在于低延迟和高分辨率需要较高的渲染速度,而VR设备往往渲染性能较低。
菲茨定律(Fitts's Law):在简单的定点任务上模拟人的运动,100篇学术论文(选择任何你喜欢的移动设备),20世纪90年代和2000年代的大多数VR结果处理的延迟为30ms-200ms。“性能”峰值约为30ms,低于30ms更“自然”,但速度较慢(在此任务中),假设运动系统具有“潜伏期”:
VR的延迟亦即Motion-to-photon的延迟,涉及多阶段:动作、传感器、处理与合并、渲染、Scanout、传输、像素变化时间、像素余辉。
将延迟保持在低值是提供良好虚拟现实体验的关键,目标是< 20毫秒,希望接近5毫秒。延迟减少方法概述将以下策略结合使用,以减少延迟并将任何剩余延迟的副作用降至最低:
- 降低虚拟世界的复杂性。
- 提高渲染管线性能。
- 移除从渲染图像到切换像素的路径上的延迟。
- 使用预测来估计未来的观察点和世界的状态。
- 移动或扭曲渲染图像,以补偿最后一刻的视点错误和丢失的帧。
渲染延迟- 时间扭曲(TimeWarp):将渲染重新延迟到后面一个时间点,与变形同时进行,减少感受到的延迟,负责DK2滚动快门,SDK(如Oculus VR SDK)可以处理方向、位置。在帧结束前,使用传感器是否有其它方式?时间扭曲– 预测的渲染(John首创)。
从引擎的角度来看,减少延迟的一种方法是使用延迟上下文在多个线程上异步构建命令列表(又称命令缓冲区),作为即时上下文,在命令缓冲区中将命令排队时会产生渲染开销,相比之下,在回放期间,命令列表的执行效率要高得多,适用于“通道”的概念。多上下文渲染允许GPU在帧中更早地开始处理,从而减少延迟。
单上下文(上)和多上下文(下)渲染的对比。
为每只眼睛的视图并行创建和提交命令列表,立即减少CPU帧时间,如果引擎受CPU限制,意味着帧延迟会立即减少。如果引擎是GPU受限,但GPU因为启动得更早,故而在帧中完成得更早,也意味着帧延迟立即减少。是否有任何特定于虚拟现实的方法来应对延迟?采样跟踪数据和使用该数据渲染帧之间的时间需要尽可能短,不要使用超过两倍的缓冲,使用最新的方向数据重新投影图像可以改善明显的延迟和帧速率。尽可能降低被跟踪外围设备的延迟,是否有任何特定于平台的方法来应对延迟?
如果仍受CPU限制,也许Compute可以帮助将可并行任务卸载到GPU上。如果仍然受限于GPU,Compute允许我们从不同的、更通用的角度来思考GPU任务,在GPU未被充分利用的地方使用它,阴影渲染通常需要顶点/几何体,因此它是安排异步计算任务的好地方。
1 Prediction
带有Project Morpheus的PlayStation 4是一个已知的系统,硬件中存在任何延迟,库/软件中存在任何延迟,需要想方设法减少这些延迟。提供CPU和GPU性能分析工具,使开发者能够计算并减少游戏中的延迟,可以用它来预测图像显示时HMU的位置。减少引擎延迟是关键,但使用预测来掩盖任何微小的剩余延迟都可以很好地发挥作用,指定的预测量越小,其质量越好。
目标是尽可能缩短HMD和控制器变换的预测时间(渲染为光子)(精度比总时间更重要),低持久性全局显示:在11.11毫秒帧中,面板仅点亮约2毫秒。
上面的图像不是最佳的VR渲染,但有助于描述预测。
管线架构:渲染当前帧时模拟下一帧:
在提交之前,会重新预测转换并更新全局cbuffer,由于预测限制,虚拟现实实际上需要这样做,必须保守地在CPU上减少大约5度。
等待VSync:最简单的VR实现,在VSync之后立即预测,模式#1:Present(),清除后缓冲区,读取像素;模式#2:Present(),清除后缓冲区,在查询上自旋转(spin)。非常适合初始实现,但避免这样做,GPU不是为此而设计的。
“运行开始”的VSync:怎么知道离VSync有多远?很棘手,图形API并不直接提供这一点。Windows上的SteamVR/OpenVRAPI在一个单独的进程中,在调用IDXGIOutput::WaitForVBlank()时旋转,记录时间并递增一个帧计数器。然后,应用程序可以调用getTimeSincellastVsync(),该函数也会返回一个帧ID。GPU供应商、HMD设备和渲染API应该提供这一点。
“运行开始”的细节:要处理坏帧,需要与GPU部分同步,在清除后缓冲区后注入一个查询,提交整个帧,在该查询上旋转,然后调用Present(),确保在当前帧的VSync的正确一侧,现在可以旋转直到运行开始时间:
为什么查询题很关键?如果有一帧延迟,查询将在下一帧的VSync右侧,确保预测保持准确(下图橙色部分):
开始运行总结:具有一个稳定的1.5-2.0毫秒GPU性能增益!正常情况,可以分别在NVIDIA Nsight和微软的GPUView中看到下图所示:
2 Timewarp(TW)
时间扭曲的想法在VR研究中已经存在了几十年,但John Carmack于2014年4月将该特定功能添加到Oculus软件中。Carmack在2013年初首次写下了这个想法,甚至在Oculus DK1发货之前。标准时间扭曲本身并没有实际帮助提高帧速率,也不是有意的,是为了降低VR的感知延迟。Oculus DK1之前的VR的延迟比今天高得多,主要是由于软件而非硬件。Timewarp是Oculus使用的多种软件技术之一,用于将延迟降低到不明显的程度。
Timewarp会在将已渲染帧发送到HMD之前重新投影该帧,以表达头部旋转的变化。也就是说,在帧开始渲染和完成渲染之间,它会沿旋转头部的方向以几何方式扭曲图像。由于这只需要重新渲染所需时间的一小部分,并且帧会立即发送到HMD,因此感知延迟较低,因为结果更接近用户应该看到的内容。
如今,所有主要VR平台都使用时间扭曲的概念。所以,与通常的看法相反,即使达到了全帧速率,仍然会看到被重投影的帧。
假设VR的目标帧率是90fps,因此大约10毫秒,任何GPU停顿都会扼杀体验,如果不能达到目标帧率,时间扭曲就会发生,fps会减半。上下文优先级通过GPU抢占使VR平台供应商能够实现异步时间扭曲。上下文优先级是NVIDIA提供的一个低级别功能,它使VR平台供应商能够实现异步时间扭曲。这样做的方式是通过使用高优先级图形上下文启用GPU抢占。
Timewarp是Oculus SDK中实现的一项功能,它允许我们渲染图像,然后对渲染图像执行后处理,以根据渲染期间头部运动的变化对其进行调整。假设我已经渲染了一个图像,就像你在这里看到的,使用一些头部姿势。但当完成渲染时,玩家的头已经移动了,现在看起来方向略有不同。时间扭曲是一种移动图像的方法,作为一种纯粹的图像空间操作,以补偿这一点。如果我的头向右,它会将图像向左移动,依此类推。由于图像空间扭曲,时间扭曲有时会产生较小失真,但它在减少感知延迟方面却非常有效。
下图是有无时间扭曲的对比图:
启用时间扭曲后(下),我们可以在vsync之前几毫秒重新采样头部姿势,并将刚刚渲染完成的图像扭曲为新的头部姿势,使得我们能够在很大程度上减少感知延迟。
如果使用timewarp,并且游戏以稳定的帧速率渲染,那么这就是几个帧上的计时效果。下图绿色条表示主要的游戏渲染,当玩家四处移动时,需要花费不同的时间,不同的对象会显示在屏幕上。在对每一帧进行游戏渲染之后,会等到vsync之前,再启动时间扭曲(由小青色条表示)。只要游戏在vsync时间限制内持续运行,就非常有效。
3 Async Timewarp(ATW)
在VR渲染中,内容的复杂性各不相同。因此,复杂内容的渲染可能无法在一帧的刷新周期内完成。因此,屏幕刷新后不会生成新内容,用户会将其视为冻结。为了解决这个问题,业界提出了ATW渲染技术。该技术通过姿势预测确定帧中的头部姿势,在生成前一帧图像时根据姿势计算姿势差,根据姿势差更改前一帧图像的位置,并在新帧中生成中间图像,解决了由于缺少当前帧而导致的冻结问题。ATW渲染技术在大多数情况下可以保证用户流畅的视觉体验。理论上,ATW可以基于一帧图像连续生成新图像。然而,由于连续的失真,生成的图像和实际渲染的图像之间的误差将累积。结果,图像质量将恶化。
然而,在vsync上,游戏从来没有100%稳定运行。PC操作系统根本无法保证这一点。每隔一段时间,Windows就会决定开始在后台或其他地方为文件编制索引,而你的游戏就会被耽搁,产生一个卡顿(hitch)。卡顿总是令人讨厌,但在虚拟现实中,它们真的很糟糕。将前一帧卡在头显设备上会导致瞬间眩晕。
这就是异步时间扭曲(Async Timewarp,ATW)的用武之地,想法是让timewarp不必等待应用程序完成渲染。Timewarp的行为应该像GPU上运行的一个单独的进程,它会在vsync、每个vsync之前唤醒并完成它的工作,无论应用程序是否完成渲染。如果我们可以做到这一点,那么只要主渲染过程落后,我们就可以重新扭曲前一帧。因此,我们不必忍受HMD上的图像卡顿;即使应用程序挂接或丢弃帧,我们也将继续进行低延迟头部跟踪。
NVIDIA支持高优先级图形上下文,抢占其他GPU工作,主渲染——正常上下文,时间扭曲渲染——高优先级上下文。当前GPU支持绘图级别抢占(preemption),只能在绘制调用边界处切换!长绘制会延迟上下文切换。仍尝试以原生帧速率(90 Hz)渲染!更好的体验是异步时间扭曲是一个安全网,长的绘制可能导致卡顿,拆分时间大于1ms左右的图形,繁重的后处理在屏幕空间分割。
NV还支持直接模式,防止桌面扩展到VR头显,从操作系统隐藏显示,但让VR应用程序直接渲染到其中,以获得更好的用户体验。对于前置缓冲区渲染,D3D11中通常无法访问,但直接模式允许访问前缓冲区,启用低级别延迟优化,vblank期间渲染,存在光束竞争(beam racing)。
异步时间扭曲采用了相同的几何扭曲概念,并使用它来补偿丢失的帧。如果当前帧未及时完成渲染,ATW将使用最新的跟踪数据重新投影前一帧。它被称为“异步”,因为它与渲染并行发生,而不是在渲染之后发生。在知道真实帧是否会按时完成渲染之前,合成帧已准备就绪。
ATW于2014年末首次在Gear VR Innovator Edition上发布。然而,直到2016年3月Rift consumer发布,它才在PC上可用。该功能对最近GPU中添加的硬件功能的依赖是Rift不支持GeForce 7系列卡或R9系列之前的AMD卡的原因之一。2016年10月,Valve向SteamVR添加了一个类似的功能,他们称之为异步重投影。该功能最初仅支持NVIDIA GPU,但在2017年4月增加了对AMD GPU的支持。下面三图阐述了不同模式的游戏循环对比图:
上:基础游戏循环。
中:当帧速率保持不变时,这种体验感觉真实且令人愉快,当它没有及时发生时,会显示前一帧,可能会使人眩晕,中图显示了基本游戏循环中的抖动示例。
下:ATW是一种稍微移动渲染图像以调整头部运动变化的技术。虽然图像已修改,但头部移动不多,因此变化很小。此外,为了解决用户计算机、游戏设计或操作系统的问题,ATW可以帮助修复不规则或帧速率意外下降的时刻。下图显示了应用ATW时帧下降的示例。
4 Interleaved Reprojection(IR)
在将异步重投影添加到SteamVR之前,Valve的平台具有交错重投影(Interleaved Reprojection,IR)。与ATW一样,IR不是一个始终开启的系统,而是由合成器自动打开和关闭。当一个应用程序在几秒钟内持续丢弃多个帧时,IR强制应用程序以半帧速率(45FPS)运行,然后每秒钟合成一帧,因此“交错”。交错重投影实际上比异步重投影有一些感知上的优势,因为它使任何双图像伪影在空间上保持一致。随着2018年SteamVR运动平滑技术的发布,交错重投影技术已经过时。
5 Asynchronous Spacewarp(ASW) / Motion Smoothing
时间扭曲(当前)和重投影仅用于旋转跟踪。它们不考虑头部的位置移动,也不考虑场景中其他对象的移动。2016年12月,Oculus发布了Asynchronous Spacewarp(ASW)来解决这个问题。ASW本质上是一种快速外推算法,它使用前一帧之间的差异(即运动)来估计下一帧应该是什么样子。尽管有名称,ASW并不总是启用的。就像SteamVR过去的交错重投影一样,当一个应用程序在几秒钟内持续丢弃多个帧时,ASW会自动启用。然后,它强制应用程序以半帧速率(45FPS)运行,并每秒合成生成一帧。因此,ASW不能取代ATW,ATW始终处于活动状态,ASW在需要时启动。
由于ASW仅具有帧的颜色信息,而不了解对象的深度,因此图像中通常存在明显的瑕疵。2018年11月,Valve为SteamVR添加了一个类似的功能,他们称之为运动平滑。
Asynchronous Spacewarp 2.0是ASW即将进行的更新,通过结合对深度的理解,大大提高了该技术的质量。在宣布该技术时,Oculus展示了以下场景,作为2.0更新将消除的视觉瑕疵的示例:
然而,与迄今为止所有其他技术不同的是,ASW 2.0不能仅在任何应用程序上运行。开发人员必须在每一帧提交深度缓冲区,否则将退回到ASW 1.0。谢天谢地,虽然Unity和Unreal Engine共同驱动了绝大多数VR应用程序,但现在在使用Oculus集成时默认提交深度。
6 Positional Timewarp (PTW)
PTW是即将对Asynchronous Timewarp(ATW)进行的更新,ATW将使用ASW 2.0用于添加高质量位置校正的相同深度缓冲区。像今天的ATW一样,PTW更新仍将始终处于启用状态,因此一旦帧丢失,合成帧就会及时准备就绪。Facebook声称,PTW使ASW启用或禁用的过渡更加无缝,因为事先不再存在位置抖动。但就像ASW 2.0一样,PTW只适用于提交深度缓冲区的应用程序。据称,PTW将与ASW 2.0进行相同的更新,因为ASW 2.0将不再考虑HMD的移动,这完全取决于PTW。
简而言之,以下是每种技术的作用:
- 时间扭曲:降低感知延迟。
- 异步时间扭曲/重投影(ATW):旋转补偿丢失的帧。
- 异步Spacewarp(ASW)/运动平滑:当帧速率较低时,将应用速度降低到45FPS,并通过外推过去帧的运动,每2帧合成一次。
下面是每种技术的比较:
“自动切换”技术并不总是启用。相反,当合成器注意到帧速率已低达数秒以上时,将启用其中一种模式。启用后,合成器会强制正在运行的应用程序以半帧速率(当前HMD为45 FPS)进行渲染。合成器在分析之前的帧并结合HMD跟踪数据的基础上,合成其他帧。当GPU利用率再次降低时,合成器将禁用该模式并将应用程序返回到90 FPS。
7 优化延迟和滞后
虽然开发人员无法控制系统延迟的许多方面(例如显示更新率和硬件延迟),但确保VR体验不会延迟或丢弃帧是很重要的。许多游戏会因处理和渲染到屏幕上的大量或更多复杂元素而变慢。虽然只是传统视频游戏中的一个小麻烦,但对于VR中的用户来说可能会非常不舒服。将延迟定义为用户头部移动和屏幕上显示的更新图像之间的总时间(运动到光子),它包括传感器响应、融合、渲染、图像传输和显示响应的时间。过去关于潜伏期影响的研究结果有些参差不齐。许多专家建议尽量减少延迟,以减少不适,因为头部运动和显示器上相应更新之间的延迟可能会导致感觉冲突和前庭眼反射错误。因此,鼓励尽可能减少延迟。
值得注意的是,一些关于头戴式显示器的研究表明,固定的等待时间会产生大约相同程度的不适,无论是短至48毫秒还是长至300毫秒;然而,驾驶舱和驾驶模拟器中的可变和不可预测延迟平均时间越长,造成的不适感就越大。这表明,人们最终可以习惯一个一致且可预测的滞后,但平均而言,波动、不可预测的滞后时间越长,就越令人不安。
Oculus官方认为强制VR的阈值应为20毫秒或以下的延迟。超过这个范围,用户报告说在环境中感觉不那么沉浸和舒适。当潜伏期超过60毫秒时,一个人的头部运动和虚拟世界的运动之间的分离开始感觉不同步,导致不适和迷失方向。大量的潜伏期被认为是不适的主要原因之一。与舒适度问题无关,延迟可能会中断用户交互和状态。在理想世界中,离0毫秒越近越好。如果延迟不可避免,那么变化越大,就越不舒服,目标应该是尽可能降低和减少可变延迟。