文 | Youwei

腾讯互动娱乐 游戏客户端开发

Hierarchical Z-Buffering分层Z缓冲(HZB)对遮挡剔除研究具有重要影响,是GPU Driven Rendering Pipeline的重要剔除手段。目前部分主流商业引擎可能因为某些原因导致该技术无法完全在GPU端工作,但依然是值得探讨的。本文先介绍HZB的基本原理以及UE4在PC端的实现方式,然后介绍如何移植到移动端并分析其性能和带来的价值,以及未来还可以做的工作。

HZB的原理

一般来说,大多数基于HZB的遮挡剔除是这样的工作的:

1. 使用一些遮挡器生成一个完整的分层Z-金字塔。

z-pyramid的最低级别是一个标准的z-buffer。在所有其他层,每个z值都是上一层对应的2×2像素中最远的z。

2. 要测试的对象是否被遮挡,可以将其包围体投射到屏幕空间,并在z-pyramid中估计mip级别。将对象的包围体投影到屏幕空间。最长的边l(像素)用来计算mip等级λ。

边长越长,选取的mip等级越高。

3. 根据选定的mip测试遮挡。如果结果不明确,可以继续使用更细的mip级别进行测试。

这个选择的原因是它使成本可预测——最多需要读取和测试四个深度值。此外,这种测试可以被看作是“概率性的”,因为大对象比小对象更容易被看到,所以在这些情况下没有理由读取更多的深度值,即节省了带宽,也增加了Cache命中。

UE4的实现

UE4只在PC端进行了实现,过程大致一样,不同的是在构建层级Z缓冲上分为ComputeShader和PixelShader两种方式,然后最终剔除工作主要在CPU端进行,意味着需要回读GPU的测试结果。以下是UE4的工作流程:

1. 使用SceneDepth作为数据源构建层级Z缓冲。Mip0为第一级,大小为1024*512,总共构建10级。分为两种方式,PixelShader方式比较简单,一次构建一级,总共执行十次。ComputeShader则稍微复杂一点,利用GroupMemoryBarrierWithGroupSync,每次同时构建4级,总共只要执行3次,便完成构建。

2. 场景中的物体经过视锥剔除以后,剩下的会被收集起来,存放在一个数组中,并且每个物体会保存自己在数组中的索引值。然后,创建2张RGBA32格式的贴图,一张存放物体包围盒的质心坐标,一张存放物体包围盒的大小。每次从数组中取64个物体作为一组,保存到贴图的64个像素区域中。

3. 采样第二步中的贴图,获取物体的质心坐标和包围盒大小,可以计算出物体包围盒的八个顶点的世界位置,对这八个顶点进行投影,选取其中最近的Z值。根据投影后的矩形区域,选取最长边长计算mipmap等级,然后在矩形区域内采样该mipmap的16个像素,选取其中最远的Z值。如果包围盒最近的Z值比它还小,则物体不可见。将结果保存到一张格式为RGBA8的RenderTarget中,作为下一帧读取。

4. 当前帧读取上一帧的贴图数据到一个数组中。每个物体将上一帧保存的数组索引到该数据进行查询,查询结果决定了该物体在当前帧的可见性。

以上步骤看出,UE4的HZB实现流程没有完全放在GPU端执行,在下一帧的时候需要回读上一帧的结果,然后进行查询以决定物体当前帧的可见性。另外,在第三步中,计算最远Z值时,采样了矩形区域内16个像素,而不是4个像素。

移动端的实现

移动端的HZB方案大部分可以与PC端共用一套逻辑实现,然后针对移动端性能点进行优化。移动端需要解决的第一个首要问题就是SceneDepth的获取,因为它是构建层级Z缓冲的重要数据来源。

在移动端上,一般来说,如果存在后处理材质需要访问场景深度信息,UE4会将场景线性深度值保存在SceneColor的Alpha通道。而对于透明材质,如果使用了DepthFade材质节点,则会通过移动设备扩展API来直接提取FrameBuffer中的深度信息。

那我们如果想在移动端上直接访问深度纹理的话,需要怎么做了?可以通过设置r.Mobile.ForceDepthResolve为1来始终保留移动端的深度信息。强制深度解析,为设备保留深度纹理。

移动端获取深度纹理

获取了深度纹理,就可以开始构建层级Z缓冲。考虑到移动设备的兼容性,这里只使用了PixelShader方式,依然构建了10级mipmap。为了保证深度值的精度,这里将每个深度值编码到rgba8888格式的贴图中。

移动端构建层级Z缓冲

构建完层级Z缓冲以后,接下来就是进行遮挡测试。算法沿用了PC端的方式,将结果保存在贴图中,下一帧回读。移动端上回读GPU贴图数据,需要注意的是,UE4会默认处理上下翻转。因为这里只是存放遮挡结果的数据贴图,所以不需要做上下翻转。

最后一步遮挡查询,过程和PC端一样,每个物体用上一帧的数组索引去查询自己当前帧的可见性。

优化读取性能

移动端回读GPU数据相当耗费性能,我们可以来看下glReadPixels分别在oppo手机型号为r15和r17上的测试结果:

r15耗时6~8ms

r17 耗时 16~20ms

直接调用glReadPixels相当慢,不过,好在目前大多数移动设备的opengles已经达到3.0以上,所以,可以考虑使用PBO的方式进行优化,使用 glMapBufferRang进行读取。过程大致如下:

1.初始化2个buffer。

2.buffer1用于异步glReadPixels读取,buffer2用于glMapBufferRange读取。

3.下一帧交换buffer,buffer1用于glMapBufferRange,buffer2用于glReadPixels。

再来看下,优化后的测试结果:

优化后,r15耗时0.9ms

优化后,r17耗时4~6ms

硬件遮挡查询 和 HZB在oppo r15上的性能对比

经过初步优化,我们来看下,硬件遮挡查询和HZB各自在手机上的性能对比。测试分为静态物体和动态物体。新建一个场景,场景内随机生成10000个物体。在分别只开启硬件查询和只开启HZB的情况下,对帧率和被遮挡物数量的影响。UE4针对硬件查询做了Batch优化,这样可以大大降低硬件查询带来的DC开销,不过Batch只对静态物体有效。所以,需要分开测试。

动态物体:

如图,只开启了硬件查询,因为动态物体无法Batch,Occlusion queries相当高,达到987,而draw call数量达到了1450。被遮挡物体为579,可见物体为411。帧率只有18。由此可见,对于大量动态物体,硬件查询本身带来了巨大的DC开销。

如图,只开启了HZB,硬件查询的DC开销已经没有了,被遮挡物体为570,可见物体为420。Draw call为467,帧率为30。

结论:对于大量动态物体查询的场景,从被遮挡物体数量和可见物体数量两个数据指标来看,硬件查询和HZB不相上下。性能上,HZB优势明显。

静态物体:

如图,只开启硬件查询,因为静态物体的原因,硬件查询发挥了Batch的优势,Occlusion queries只有58,draw call只有511,帧率达到35。

如图,只开启了HZB,硬件查询Batch带来的DC也没有了,所以draw call略有下降。帧率为33。

结论:对于大量静态物体查询的场景,HZB仍适用,性能与硬件查询Batch相当。

硬件遮挡查询 和 HZB在oppo r17上的性能对比

同样分别测试动态物体和静态物体,r17和r15表现了完全的不一样的结果,之前在优化读取时也发现,r17依然有4~6ms的开销,这是为什么了?这跟r15和r17在硬件上不同有关。

R15硬件参数

R17硬件参数

另外,值得一提的是,UE4针对高通设备,做了硬件查询的上限限制。最大510次查询,而其他设备默认是最大4000次查询。

这导致了R17在大量动态物体场景下,即便没有做Batch,也只有不超过250的Occlusion queries数量。这并不是什么优化,而是UE4直接放弃了超过该数量的硬件查询,再加上依然存在的回读耗时,这样使得HZB的优势就不那么明显了。

如图,R17上,开启硬件查询,关闭HZB。

如图,R17上,关闭硬件查询,开启HZB。

结论:基于以上原因,在大量动态物体和静态物体场景下,HZB在R17上都表现不佳。

其他性能开销

移动端除了回读耗时以外,在HZB构建以及遮挡测试阶段,性能消耗也不能忽视,特别是采样16次贴图的操作。如下分别是在R15和R17上的测试结果:

R15在构建和测试阶段分别耗时2.6ms和1.6ms。回读耗时0.9ms

R17在构建和测试阶段分别耗时0.48ms和0.41ms。回读耗时5.2ms

通过对比发现,R15回读快,而构建慢。R17回读慢,而构建快。不同的硬件架构带来的性能差异很大,关于移动硬件分析已经不属于本文探讨范畴了,在这里就不展开讲了。

总结

关于UE硬件查询的Batch结论:

1.动态物体不会做Batch,全部一个一个去查询,带来巨大的DC开销。

2.静态物体在被遮挡的情况下会做batch查询,DC显著减少。

3.高通手机最大查询次数为510,其他为4000,而实际推荐最佳查询次数是250,2000(分别除了2)。

移动端HZB结论:

大量动态物体查询,HZB适用于非高通移动设备上。

大量静态物体查询,HZB仍适用于非高通移动设备上,性能与硬件查询Batch相当。

针对移动端未来可以做的优化方案:

1.将物体数据由质心坐标+包围盒范围改为质心坐标+包围球半径,可节省一张RGBA32贴图。

2.将16次采样改为采样包围球表面最近点和包围盒四个顶点,可减少11次采样。甚至更保守点,直接将包围球面最近点作为采样点进行比较。

3.高通设备可以考虑使用vulkan图形API进行数据回读。

4.高通设备在构建HZB的时候可以考虑使用ComputeShader。

delphi dbgrid 上下移动并获取选中值_如何在UE4移动端中实现HZB?相关推荐

  1. 获取异常信息_如何在 ASP.NET Core 中实现全局异常拦截

    异常是一种运行时错误,当异常没有得到适当的处理,很可能会导致你的程序意外终止,这篇就来讨论一下如何在 ASP.Net Core MVC 中实现全局异常处理,我会用一些 样例代码 和 截图 来说明这些概 ...

  2. python一次输入多个值_如何在Python的一行中从用户输入多个值?

    为了读取语言C中的多个变量,我们编写类似-//Read three variable in one line scanf("%d %d %d", &x, &y, & ...

  3. JQuery判断radio是否选中并获取选中值的示例代码

    这篇文章主要介绍了JQuery判断radio是否选中并获取选中值的方法,代码很简单,但很实用,需要的朋友可以参考下 其他对radio操作功能,以后在添加.直接上代码,别忘记引用JQuery包 ? 1 ...

  4. input checkbox 获取选中值和设置选中状态

    获取选中值代码: <input type="checkbox" value="1" name="assess_mode" > & ...

  5. Material UI 带复选框表格获取选中值(索引)

    vue+Material UI 带复选框表格获取选中值(索引) 发现问题 解决 写在最后 发现问题 神坑UI框架–Material,这款框架我不晓得为什么会这么坑,有这么多的问题为什么会被推出来,真的 ...

  6. js、jquery操作复选框checkbox总结(单个/多个获取选中值、初始化设置默认选中值、全选反选)

    一.单个复选框 使用label标签可以点击文字就能选中复选框或者是取消选择复选框 label标签的两种使用方法:   https://blog.csdn.net/qq_40015157/article ...

  7. jQuery设置radio默认选中状态及获取选中值

    jQuery设置radio默认选中状态及获取选中值 //获取选中值 var rechargeType = $("input[name='rechargeType']:checked" ...

  8. android 选中的方法,Android之RadioGroup获取选中值的方法

    Android之RadioGroup获取选中值的方法 发布时间:2020-11-12 17:22:08 来源:亿速云 阅读:466 作者:Leah Android之RadioGroup获取选中值的方法 ...

  9. JQuery/JS select标签动态设置选中值、设置禁止选择 button按钮禁止点击 select获取选中值...

    //**1.设置选中值:(根据索引确定选中值)**// var osel=document.getElementById("selID"); //得到select的IDvar op ...

最新文章

  1. 众里寻他千百度-- 轻量级持久化框架
  2. python获取当前服务器ip_Python实现获取域名所用服务器的真实IP
  3. Java计算时间差、日期差总结(亲测)
  4. android.intent.category.DEFAULT和LAUNCHER
  5. springCloud五大组件--Eureka
  6. 在基于or1200处理器的SoC上移植linux
  7. 深入探索多线程优化方法
  8. python importsystem
  9. qlineedit 设置界面不能输入_一个类似QQ的登陆界面为什么两个输入QLineEdit的上下距离太远...
  10. 使用Maven插件对项目进行打包
  11. 私人定制-代码生成器3
  12. 从程序员到项目经理(24):慎于问敏于行 - 忠于工作不等于奴性
  13. 存储单位--MBR-GPT
  14. Centos 安装配置 Dynamips
  15. foobar2000 for mac(经典音乐播放器)
  16. 生物信息学中用到的计算机知识,生物信息学期末复习知识点总结
  17. RequestHead详解
  18. U3D学习项目一:2D横版小狐狸闯关游戏(代码部分一)
  19. python求加速度_【掌控】mpython-加速度-水平仪 - DF创客社区 - 分享创造的喜悦
  20. 【项目部署】使用Jenkins一键打包部署前端Vue应用

热门文章

  1. 最全 Neo4j 可视化图形数据库的工具!
  2. 基于Transformer对透明物体进行分割
  3. Classifying dynamic textures via spatiotemporal fractal analysis(许教授)
  4. 邮件服务器 之 基于FreeBSD和Postfix的邮件系统与邮件列表的web mail安装
  5. Nginx HTTP负载均衡和反向代理配置
  6. scau 1142 巡逻的士兵(分治)
  7. 大型网站技术架构(3):WEB 前端性能优化
  8. 推荐65个以自然风光为背景的UI设计
  9. java.lang.Class
  10. modlesim使用