Ps.《Robo Recall》被誉为是《Bullet Train》的精神续作。
以下是Nick Whiting演讲内容整理:
大家好,我是Epic Games VR&AR项目部技术总监Nick Whiting,也是《Robo Recall》开发团队负责人。大家可以看到Epic一直有自己尝试做VR的内容,从Oculus四年前刚开始给出第一个硬件的时候就一直联系我们,我们就开始做VR游戏。我们希望使用我们的工具做引擎,改善工作流程和改善引擎效率,使得帮助开发者更好的使用引擎开发高质量的VR内容。
怎么样的对齐方式是最好的?其实我们也还没有定论,最后的结果就是在两者之间,模型和手柄都是差不多可以对上。在做游戏测试的时候,大部分用户有些手偏高或偏低,两者之间恰合适。另外,我们需要做的是进入游戏先让玩家打靶调节准心,比如玩家始终瞄的有一些偏高,我们会帮玩家往下修正一点,在整个游戏中我们都会帮玩家往下修正一点,反之亦然。
枪的三种交互:重量、后坐力、抓取,《Robo Recall》如何思考?
就枪本身来说,有三部分的交互。第一,物理感,即握枪的重量感。第二,开枪的过程中有一些反馈,比如硝烟的味道,力的反馈。第三,电影中各种枪的杂耍动作。
传统中直接把引擎设置成物理对象的好处
直接使用引擎自带的模拟功能,能把枪作为一个Physics,只要玩家握着手枪的时候就设置完。另外,后坐力模拟直接加了一个物理的Impulses,能模拟真实情况下受到冲击的感觉。此外,把它设置成物理对象的好处是碰撞交互之类的,跟其他枪、物体碰撞的声音,以及给用户的力反馈感都是自然就有的。
传统中直接把引擎设置成物理对象的不足
当把它设置成物理对象时,枪可能会卡在某些地方,尤其枪大的时候,卡在桌角之类的就会不停抖动,有时候会突然再弹回来,这样会很不好,破坏了我们使用物理的方式。与其用引擎来做模拟,我们不如自己决定哪些地方有物理模拟,哪些地方不要。
《Robo Recall》中道具枪的交互方式
首先,VR里都想要降低延迟,但是对于枪是有问题的,因为枪是有重量感的,如果过快地挪动是不太可能的,所以反而加一些延迟。我们把前期帧的速度累计起来,让它更有重量感。虽然没办法让实际的物体变重,但是可以让玩家感受到好像它的重量变化了。
其次,后坐力不能用物理来加,我们需要用其他的手段Timelife功能。为什么用它不用线性的?因为手被后坐力往上玩家就会想压下来,所以开始比较快后面比较慢,不是线性的过程。游戏里加上Recall后的效果,全速的时候可以看到每枪开完后坐力往上之后很快就恢复。《Robo Recall》里的枪恢复时间比较慢,很明确地看到当玩家连续开枪的时候它是慢慢往上走的。
《Robo Recall》里所有的枪具有同样的功能,只不过我们针对不同枪有一些参数的微调,比如手枪回复速度比较快,因为比较轻比较小;AK回复速度比较慢,双手持枪后坐力的影响和回复都会比较小一点。但我们也有暂时还没有完成的东西,我们希望枪能跟虚拟世界里面的物理对象有其他的交互。
最后,在游戏中各种玩枪的动作,即抓取系统。我们大概花了足足六个月来做好抓取系统,
刚开始采用的方式类似于Oculus Toybox,当玩家去抓取的时候会有一个球体,这个球体的大小跟你的手掌展开差不多大, 按抓取键的时候,我们会去判断这个球体跟物体有没有发生接触。
但在抓取过程中,我们还发现了一些问题。用户经常会很远距离去尝试抓他肯定抓不到的东西,或者说对于深度判断不是那么准确,明明没碰到,就认为碰到,按抓取键。所以我们加入了一些感知的反馈系统,因为人类的感知系统是很敏感的,一旦反馈给他,他能比较清楚地知道发生了什么情况。比如,通过振动反馈,手慢慢接近物体时,就开始有微小的振动,随着距离越来越接近,振动感越来越强劲。这样能很好地判断玩家的手是不是离它越来越近。另外,通过颜色来区分。用一个后处理材质把用户当前能抓到的东西高亮表现出来,当他发现哪个东西变成黄颜色,就会按下抓取键。即使不是玩家想要的,经过大脑的反馈以后,也可以抓过来扔掉,再去抓想要的东西。
第三个问题是朝向的标准。比如说如果我们把抓取的延伸的朝向按照你手的朝向来做,那如果玩家是瞄着它用手来,其实是很简单的。但是当物体在玩家手斜下方时,玩家是抓不到的。如果知道机制是工作的,其实是有很好体验的,但如果不知道,玩家就会很厌烦。
我们尝试的方案是通过从眼睛到手生成的方向来计算延伸出去的位置。但是如果从侧面这样拿东西,手从侧面来看是偏的,这样又是拿不到的。虽然这个方法看起来已经好了很多,但还是存在我刚刚说的某些情况下导致用户的预期没有办法被满足。
最终我们的解决方案就是把刚刚算出来的两个朝向取了一个平均值。我们希望能有更高级的方案,比如说能有眼球追踪,我知道玩家注视着什么,从而知道玩家现在关注的是哪样东西,然后就把它抓起来。
《Robo Recall》里有一个很重要的设计是同时可以抓这些机器人,那么第四个问题是机器人被抓以后像死掉了一样,看起来没有任何反抗。基于此我们加了物理动画的功能。比如在《Robo Recall》里面,那些机器人被枪打到的时候,他除了在做正常的动画,也会有受物理冲击以后的物理效果,两者可以混合到一起。
我们要潜在的帮助用户。比如在《Bullet Train》里最后关卡设计的Boss,玩家需要放导弹。玩家只要抓住导弹,用导弹瞄准Boss,然后松手就可以。
我们观察那些失败的行为,他们并不是瞄准直接放手就好了,而是像扔篮球一样。所以我们的解决办法是把它做成像真的在扔导弹一样。于是我们把用户扔的时候那个手位置的轨迹记录下来一部分,然后来平均计算出他出手的位置,我们故意做了一些扰动,让它看上去像是被你扔了以后慢慢朝向前面。这样以后其实大部分人都已经能扔中Boss了。
但还是有一些特别的用户可能还是会有问题。但当发生这个情况的时候,玩家每失败一次,我们会微调一个系数帮他自动标准一点。所以第二次扔,就会慢慢地看到它帮你修正的更接近Boss一点,最后随便怎么扔也能扔中。但是如果开始就打开辅助系统,用户是不爽的。在修正过程中,前半段还有个0.5秒左右的时间按照玩家扔出去的方向再慢慢修正的。却给玩家的感觉就是好像越扔越准,其实他一直没扔准过。
这个准则其实是你扔的时间越长,我们帮助玩家扔准的辅助越多。
虚拟空间移动的解决方案
《Robo Recall》有非常好玩的机制,接下来的问题是怎么在虚拟空间环境里移动?比较大的问题是玩家需要对准Oculus的Tracking系统,一旦转身,模型可能被自己的身体遮蔽掉。我们想尽可能避免让用户转来转去,既然在游戏里面看到这种分离的手和头,不是一个完整的人,那为什么不能传送?
《Robo Recall》的场景相当复杂,高低的地方甚至小的拐角玩家都可以到达,所以我们希望有一些其他系统来帮忙。于是我们加入一个功能叫带方向的传送,即玩家瞄准的方向就是传送的方向。
Hidden Area指我们在周边,最后没有用的那些地方,先在裁减面画出来,最后所有的Test都会失败,接下来的操作、计算都不会发生。Visible Area Mesh是反向,可见区域画出之后,在做后处理的时候,实际的计算处理就变成了Mesh区域,象素处理变少,会进一步提高效率。这两个东西一起结合起来我们在《Bullet Train》的Demo里面节省了相当多的GPU时间,在Oculus上面节省了0.25-0.3ms。
全效渲染和延迟渲染在游戏上的对比
接下来我们要讲一些功能在4.14里面有的场景,主要我们希望能使用同样的《Robo Recall》表现不同的环境效果,比如一个城市有夜景也有白天的场景,我们尽可能用静态的光照来做场景的光照环境。因为都是静态的光照所以相率还是相当高的。
全效渲染器是怎么回事?在《Bullet Train》的时候我们用的还是一个延迟渲染器,因为有GBuffer可以做特别的技巧,所以可以用GBuffer里面的各种信息和技巧。比如《Bullet Train》里面我们关掉Scene,通过扰动做出了这种反射效果。
还有就是这个火车刚开始的场景,本来一开始我们用75个动态光非常费,后来我们因为可以访问到GBuffer,只用了一个动态光把效果做到了现在这样。在我们做《Bullet Train》的时候,Oculus也做了一个基于UE4的全效渲染器,我们也参考了他们的全效渲染器做了我们自己的版本。
比较一下我们自己的全效渲染器和延迟渲染器,最大的区别是抗锯齿部分,延迟渲染器我们只能用TTA,在这种本来分辨率不是很高的VR环境下会显得更加模糊、不太清晰。然而这种情况下可以访问整个GBuffer,从功能性角度来讲是最完善的,所有的引擎渲染功能你都可以用得到。
用全效渲染器可以选择用TAA还是用MSAA,在《Robo Recall》里我们有很多直线条的街道,MSAA会带来非常好的帮助。全效渲染器总体来看是更快一点的,但是是损失了屏幕空间的效果之类的,但是全效渲染器能够适配更低一些的硬件。
Oculus的实现是完全独立的Render的分支,我们的做法是在我们的延迟渲染器加了一个前向的path,你可以把延迟的path关掉,或者同时开启延迟和前项。这样的好处可以在全效渲染器用一些延迟渲染的内容。
在一块Nvidia GTX 970上的测试,可以看到延迟管线延迟是12.3ms,全效渲染器延迟是9.56ms,同样超采量是135% ,其中0.7秒的时间主要节省在GBuffer的带宽占用包括各个地方的拷贝之类的,接下来还有一个比较大的部分,是因为你可以单独开关某一个物体的选项。
还有一个好处是你用全效渲染器的时候渲染分辨率的调整的范围可能可以更大,因为TTA比较大,你不能延迟选项设的太小,但是全效你可以用MSAA,你可以设置Oversampling比较低一些。
其实这两个渲染器都有各自的优势,不是说哪个全效就是可以帮助你解决所有的问题,比如你要做虚拟的人类或者皮肤的效果可能还是需要用延迟渲染器的一些功能。在《Robo Recall》当中我们一开始用延迟,换到了全效渲染器,我们用了MSAA,还是有一些地方我们我要特别的注意和改进。
MSAA对于轮扩的效果还是不不错的,但是对于这种亚象素级的没有什么办法,可以看到右边的图越来越小以后就会有一圈一圈的效果,《Robo Recall》因为里面有很规整的城市,比如窗户之类的建筑,很远看过去就是有亚象素级的就会产生比较明显的效果。
一个很简单的解决方案是刚好我们这些建设是比较规则的形状,简单的方法就是把它换成一个没有什么细节,直接靠贴图。还有一个问题MSAA没法解决特别明显的闪烁的效果,TAA一直可以很好的缓释这个效果,但是MSAA不行。
CPU上的性能时间调整
刚刚说的是一些渲染的东西,接下来我们看看CPU方面开销的性能调整。第一大功能是我们的把蓝图转成C++后端的功能,《Robo Recall》将会成为第一个正式使用并发布这一个功能的项目,因为《Robo Recall》有大量的主要的Game Play都是用蓝图来写。我们这个功能能帮助你改进多少CPU时间,取决于用了多少的蓝图,在这个项目里面大部分情况下我们也只省了0.25毫秒左右,但是一个比较大的改进是不太会有那种卡顿的现象,本来用蓝图的时候有一些特别的地方会卡个将近四五十毫秒的样子。虽然这相当的有帮助,但是我们还是要做很老套的优化。
我们后来发现有两大块导致CPU的开销比较高。第一大问题是在BP里面的一些数学计算。因为我们比如枪械各种各样的计算,有大量的数据计算在蓝图里,我们开始觉得用到蓝图转到C++应该没有什么开销,直到看到生成的代码。
比如说一个App,在蓝图转向C++以后,跑在类似虚拟机的环境下其实加了很多指令,本来这个东西编译器应该可以自动消除这一行,这样就做不了。于是我们把一些特别底层重要的一些函数以及数学库里面的函数做了一些优化。大概是BP生成C++代码的优化,直接可以让它运行起来,这是一个相当大的性能改进。
另一块是将近5毫秒的时间是耗费在移动处理上。比如一个机器人在运动的时候,因为自己发生了位置的变化,所以他会更新自己的Scene Component,下面挂了一些子Scene Component,所以所有的级都要计算,并且有时候他还要去判断有些区域的碰撞之类的情况,开销相当大。
《Robo Recall》里一个很重要的功能就是交互,所以我们专门做了一个Component,而且Scene Component上面会挂一些碰撞和其他的Component。我们来看一下比如双组的机器人,分了头、躯干、双手、双脚,总共加起来有五十个Scene Component,是相当多的。
大部分时候用户并不会跟那么多机器人都发生交互,所以更新那么多非视觉的Component是很浪费的。所以大家可以看到这条黄线所在的区域是你用户周围的人触手可及的区域,这个区域内我们如果敌人跑到这个区域我们才会把所有的Scene Component放到root上去更新,而在黄线之外的Component都不需要更新。
Component功能写到专门的Component里,根据距离决定是不是要把其中的一部分没有必要的Scnens Component省略掉。最终省了4毫秒,我们发现有一些枪在做中间版本加了很多不必要的Component,最后有二十几个,最后减少到五六个。
画面再优化的三个细节:超级视觉裁减+关注点渲染+monoscopic
在发布前还有什么我们还没有做的打算做的?其中之一叫超级视觉裁减,现在的视觉裁减会在每只眼睛各做一遍,还是有一些额外的开销的。我们打算把两只眼睛看到的东西都算进去计算一遍,预期可以节省一毫秒左右。
还有一个就是叫棋盘格的关注点渲染,其实中间注视的地方渲染可能比较高,其实分辨率是不变的,只不过周围用一些棋盘格的样子,然后实际的象素计算会比较少一些。
另外一个叫monoscopic Rendering,是双眼看到的东西不太一样,即近景不太明显,但是远景比较明显。比如,八米以外位置差别是小于一个象素的,第三个摄像机单独裁减一遍,双影化的都是近景的物体,再跟远景质化一遍的物体合成起来,我们在已经有代码了,在移动端的VR上面看到四分之一的提升,接下来我们打算挪回桌面渲染器。
关注微信公众号:VR陀螺(vrtuoluo),定时推送,VR/AR行业干货分享、爆料揭秘、互动精彩多。关注微信公众号:VR陀螺(vrtuoluo),定时推送,VR/AR行业干货分享、爆料揭秘、互动精彩多。
投稿/爆料:tougao@youxituoluo.com
稿件/商务合作: 六六(微信 13138755620)
加入行业交流群:六六(微信 13138755620)
元宇宙数字产业服务平台
下载「陀螺科技」APP,获取前沿深度元宇宙讯息