目前VR行业发展迅速,VR性能逐步完善。但目前VR仍然存在一些技术瓶颈。 VR渲染效率和延迟是导致晕动症和人眼疲劳最重要的两个问题。本文通过UE4项目开发中的一些流程和技巧来优化VR实时渲染效率。达到提升渲染效率降低延迟,从而降低导致晕动症和人眼疲劳的机率。本文是《使用UE4开发VR项目-性能优化》的第二篇。希望能和您分享一下在UE4 VR项目优化的基本思路方法和技巧。
  造成晕动症的原因很多,比如设计不合理,硬件技术瓶颈。CPU、GPU上的性能问题、内容优化不合理等等。 下面通过一套优化流程来说明VR性能优化的的思路和方法。
(一)VR优化考虑的问题
(二)VR测试环境准备
(三)找到受到限制的瓶颈
(四)GPU渲染线程分析
(五)CPU逻辑线程分析
(六)可能遇到的问题和优化方法
 (一)VR优化考虑的问题
  HMD(头显)刷新频率
  目前主流的VR设备对帧率的限制都在90FPS/120FPS
  DK275 FPS
  CV190 FPS
  HTCVive 90 FPS
  PS4VR 120 FPS
VR双屏渲染
  简单的说双屏渲染就是把屏幕所见看成一个Mesh的两个Instance,可节省50%以上效率
Instanced StereoRendering
  实例化立体渲染
HMD DistortionMask
  HMD镜透变形

泰课在线

VR多分辨率渲染(Multi-Resolution Shading)
  人眼只对中间的视距中心的位置较敏感,所以屏幕中间渲染分辨率更高,屏幕周围渲染相对较低。它可以让我们在不影响最终画面显示效果的前提下,提高计算资源的利用率
 
VR SLI (Nvidia VR优化技术)
  这项技术是使用多块显卡协作,开发者可以自由定义显卡处理什么信息。
 
泰课在线
场景比例
  须使 VR 世界中的物体比例与现实世界一致。大小不匹配会引起眩晕或模拟疾病。
渲染品质压力
  比如材质复杂度、灯光数量、动态阴影、粒子系统(材质复杂度 +双目粒子面积+ 粒子数量)、贴图尺寸、双屏可见视野、模型面数等等。通过控制物件,特效数量,使用cull distance volume、instance等方式可以控制渲染量达到控制目的。
DRAW CALL数量
  比如同屏看到的Actor数量、材质ID数量、每个actor上的feature数量、灯光投影面积过大导致Draw Call偏高,CPU逻辑线程和GPU 渲染线程都会发生问题。
内存占用率
  通过限制贴图数量、尺寸、提高模型、材质、贴图的复用率、控制物件、特效数量、及UI相关资源数量,可以达到控制内存的目的。
VR包体大小(VR移动端)
  通过提高模型、材质、贴图的复用率,使用压缩限制贴图数量和尺寸,必要时可取消mipmap等方式可以控制包体大小。
卡顿问题
  我们首次加载地图的卡顿问题,视地图大小而定,若地图相对小;资源量不大,可用施明的预加载一遍的方式;若地图比较大;资源量也大,则不适用。需考虑修改newRenderPipelineState为异步来解决。另外的针对加载mipmap的卡顿点,也可以通过取消TextureStreaming来解决,取消TextureStreaming后存在显示延迟的问题,这个问题需要修改默认的显示顺序,可按照摄像机距离进行排序显示。
 (二)VR测试环境准备
  VR项目参考请参考《基于UE4的VR项目基础环境配置和Motion Controller控制配置》的VR项目基础环境配置(http://gad.qq.com/article/detail/7167914)
  创建测试环境
  暂停游戏
  跑单独游戏
  进行多次测试
  确保帧数不封顶 关闭垂直同步
 (三)找到受到限制的瓶颈
  目前的VR内容来看,CPU逻辑线程上出问题的比例很小。如果CPU逻辑线程出现了问题。一定是实现和设计上出现了问题。或者是代码写的有问题。大多数的问题出现在GPU渲染线程上。找到瓶颈的方法,第一步判断是CPU逻辑线程问题还是GPU渲染线程问题。第二步使用控制台Stat fps来看FPS,如果FPS已经满足就没必要进一步优化。如果不满足,接下来使用Stat Unit命令查看更详细数据来帮我们判断瓶颈所在。
HMD SP 100 
  SP是屏幕比例,输出分辨率是固定的。但Render Buffer可以调整。默认120% 调整成100% 时候 画面会变糊,测试VR性能过程中,根据不同关卡、摄像机位置、来调整不同的值。在渲染压力很大,不能进行进一步优化的情况下,让画面变糊,让帧数有提升后,即可进一步优化。判断是Pixel Shader瓶颈,还是Vertex Shader瓶颈,只要调整HMD SP 会有帧数提升的时候,即可确认是GPU渲染线程瓶颈。这个命令在优化压力很大的情况下是最有效果的调整。
Stat FPS
  显示FPS数据,查看当前帧数
Stat unit
  这里面有三个有用的数据,GAME:游戏逻辑线程这一帧所耗费的时间。DROW:GPU渲染线程所耗费的时间。Gpu:GPU耗费的时间。
  可以查看几个模块每一帧耗费的毫秒数。如果Game这里毫秒数最高,首先查是否限制帧数上限。如果Game和Frame一样,那么可能不是游戏主线程瓶颈。很可能是限帧的原因。
  调整渲染质量Scale Ability设置
  关闭一些stuff,然后看效率上的区别
  一些可调的show flag(屏蔽渲染查找问题)
  开/关屏幕反射
  开/关AO
  开/关AA
  开/关bloom
  开/关延迟灯光
  开/关灯光类型
  开/关动态阴影
  开/关GI
  开/关后期
  开/关环境反射
  开/关折射
  开/关贴花
  开/关半透明
  开/关tessellation
(四)GPU渲染线程分析
  如果遇到GPU瓶颈最快的验证方法是改变分辨率 降低分辨率可以极大提高帧数
  如果帧数有大幅度提高 即是GPU瓶颈。如果影响不大,可能是面数太多。
  一般GPU渲染线程问题大多数可能性是像素着色器(Pixel Shader)
  ProfileGPU 命令可快速确定多个通道的 GPU 消耗
  快捷键:Ctrl+Shift+,
r.ProfileGPU.ShowUI 对 UI 进行抑制
   建议不要在编辑器里运行,开关编辑器也是有渲染开销的。渲染数据会产生误差。最好建立单独游戏(Standalone)运行环境
几个值得注意的数据项:
Base Pass
Deferred Decals
Lighting
SSR(环境反射)
Translucency(半透明)
Postprocessing(后期处理效果)
Particle(粒子)
  **当Base Pass很高,可以使用命令行打开Early Z Pass 可以降低 Base Pass 但同时会少量增加DRAW CALL**
  检查影响GPU效率的内容查看有无超标现象
  比如分辨率、HMD SP、投影贴图大小
ViewMode检查
  当三角面密度太高(高到三角面小于2*2像素 往往发生在远处物体上) 很容易出现问题。
  分别查看三角面、顶点、灯光数量、阴影设置、Actor数量
  LOD 关闭Shadew 、灯光屏幕面积
  顶点太多
  点动画的Shader处理过于复杂
  Tessellation 过于复杂
  多重UV、过多的SG
  查看Staticmesh Editor里点和面数的差别是否大
  点没有合并、场景GPU粒子模拟数量过多
材质复杂度
  Quality、Switch、Sin、 Pow、 Cos、Divide、Noise 这些节点很耗费资源
  减少材质Shader的指令的数量。减少Texture Sample的数量:把经常使用到同一个物体上的图案合在一张贴图上;去掉对质量影响很小的贴图,比如Specular、AO等。尽量使用QualitySwitch,Sin, Pow, Cos, Divide, Noise节点。多向量的计算量总是大于单向量的计算量。
   遮挡的culling计算
  使用预算可见性剔除遮挡的对象。
延迟灯光
  当使用lightingfunction、IE、接受投影、区域光、复杂shadingmodes的时候会变得更昂贵。反射SSR如果有问题,请关掉它。另外后期AO也很耗费资源。
(五)CPU逻辑线程分析
  引起Draw Call 数量增多的原因
1、同屏Actor过多、材质复杂性过于复杂。 (合并Actor、降低材质复杂度)
2、材质ID太多、重用材质贴图(把同一材质合并成一个物体)
3、每个Actor上的Feature 太多(主要增加投影属性 增加Custom depth属性)
4、过多灯光投影(检查投影计算来自哪些物体被投影)
Mesh Draw call 可以检查静态模型统计器来解决问题
  Actor 设置 Feature 会增加DrawCall数量的是 custom depth
  通过检查工具来设置Showflag 使用Property matrix 来过滤
  使用排除法,隐藏各种元素 查找Draw Call 数量过多的因素
  隐藏HUD (HUD占用很多性能,Showflag.slate 1)
  还可以使用移动设备上的CMS增强阴影(4.12)
  Rerdring>Mobile>Combined>StaticandCSM shadrring
  接收物体选项:ReceicvCombined Static CSM shadows from stationary lights
  UE4里由于使用了延迟灯光,所以灯光的优化比前向渲染方便的多。最快速最有效的方法:使用静态光源。如果使用的是动态光源,要减小Lighting Cull、半径、衰减、 Cone大小角度等等。总之尽量减少重叠。
  投影的开销最大不是来自于PixelShader,而是来自于被投影的Mesh面数太多,还会被灯光数量,投影物体数量放大。关闭投影的灯光;减小范围或张角;减面,加LOD
  可以使用r.Shadow.MaxResolution 命令用来查看阴影分辨率数据。
Stat Game
Stat Scenerendering
  命令可以显示
  DrawCall数量 (计算公式:Mesh数量 * Mesh ID * 投影灯光数量)
  Particle粒子
  复杂的UI 
  Decal 贴花
  静态Mesh
  动态Mesh 
  查看影响最大的部分 ,再次使用Show Flag 开关渲染特性来查看这些特性对DRAW CALL和帧数的影响
DrawCall优化:
  打开Statistics统计面板,按使用次数排序Actor。查找使用次数最多的MESH 的ID ,如果ID是负数,那么就可以做优化。
  例:如果有60个MESH 资源 其中有一半使用同一资源,那么就需要考虑需要用Merge Actors(融合Actor)来降低ID数量。优先减少使用频率最高的Mesh ID。这个操作可以减少这60个MESH中50%以上的DrawCall.(优化模这步骤建议放在最后才去考虑)
  Staionary灯光和动态灯光要尽量的少,并且能不投影的尽量不要投影。并且减少光源间Overlaid(覆盖)
  控制台:r.VisualizeOccludedPrimitives 1
  命令主要用来判断场景中哪些遮挡掉的物体是被渲染的。
  绿色的框就是没有绘制的对象.,如果有Actor在遮挡后面但是并没有被绿色的框画出来,这时候引擎认为这个Actor是可见的,造成消耗。这种情况一般是边界(Bounds)设置太大,一些非Root Motion的动画要注意。当Actor从A点移动到B点,边界会自动变大。就可能出现Actor 在遮挡物体后,但引擎仍然认为Actor是可见的并将它绘制出来。
粒子优化:
StatParticle
  把占屏幕面积较大的,靠视点较近的,半透明较多的,材质复杂的,边界盒较大的,粒子数量多的进行集中优化。离视点较远的,可以使用LOD减少粒子绘制。当GPU进行立体渲染(Stereo Rendering)的时候,当视点越近,视差看到位移的位置就越远。离视点近的粒子很容易出错。引擎里的粒子大多数都是面向摄像机的一个片。当引擎进行立体渲染的时候,粒子片总是朝向左边摄像机。这时候右眼摄像机就出现了视差错误。离视点越近的粒子,立体化渲染时错误越明显。为了避免这种错误,可以把粒子做成实体模型。
后期优化:
下面是一组后期优化组合:
  Post processingsetting
  Scene color
  Fringe intensity 0
  Grain intensity 0
  Color gradingintersity 0
  Bloom setting
  LPV 0
  Ambient occlusion 0
  DOF Method Gaussian0
  Motion blur all 0
  AA FXAA
  SSR 0 Max roughness0.01
  Ambient cubemap 0
Profiler
  最后可以使用Profiler将数据捕获到磁盘上进行分析。
(六)可能遇到的问题和优化方法
最常见问题的原因包括:
  测试环境不合适,比如灯光没构建。
  双目渲染Actor太多,多材质ID
  模型面数太多并且没有使用LOD,没有合理使用剔除
  灯光类型使用没有规划,大面积动态投射阴影
  大面积使用透明
  HMD(头显)刷新频率
使用的灯光类型
  构建 VR 项目时使用静态光照 和 光照图。如需使用动态光照,应尽量减少动态灯光的数量。如场景为室外,须将定向光照设为动态而非静态,然后开启级联阴影贴图(CSM)并尽量进行简单设置,即可生成阴影。
VR & VFX
  在 VR 项目中多数情况下,需要使用静态网格体替代 2D 粒子模拟 VFX(如爆炸或烟雾拖尾)。近场特效或离摄像机非常近的特效在 VR 项目中效果上佳,但特效须由静态网格体粒子组成。
VR 和透明度
  在 3D 图形渲染中,透明度的开销非常之大,因为每帧都需对其进行整体再次求值,以确保未发生变化。正因为再次求值的存在,VR 项目中透明度的渲染开销极大,大于其带来的效果增益。然而,使用 DitherTemporalAA 材质函数可解决解决这一难点。此材质函数将使材质拥有外观上的透明度,并避免常见透明度问题(如自我排序)。
多边形减面
  在 VR 游戏中,玩家观察的自由度更高,执行减面操作可能导致玩家看到不应该看到的物体,因此不能进行此操作。
阴影投影
  去掉大部分动态着色器(DynamicShader),
  用BP控制角色脚步离地面远近来缩放脚下阴影片面大小。
  Reflection Capture  减少 尽量不要重叠
  关掉场景SSR  用材质模拟SSR效果
  打开SM联级动态阴影
创造性作假:
  三角面:
  远处mattinpaiting
  投影面片,渲染在贴图上
使用PrecomputedVisibilityVolume(预遮挡计算)
  遮挡较多的地方可以大幅度提高渲染效率
使用CullDistanceVolume(裁剪体积)
  根据Mesh在屏幕上所占像素尺寸裁剪模型
内存分配
  CUP RENDER瓶颈
  stat scenerendering
0