一、Visibility Buffer

可见性缓冲区(Visibility Buffer)是延迟渲染的进阶处理。
与延迟渲染结合起来更好理解,即:延迟渲染是将材质与光源照明计算进行解耦,基于此种思路将几何体与材质解耦:

  • 每个像素/三角形的材质仅被处理一次,并且在管线其余过程中不会处理到被遮挡的纹理、缓冲区或资源,这种整体的渲染思路便是Visibility Buffer的处理思路。

在此之前我们先来看一下为什么要使用Visibility Buffer,这就得说到延迟渲染的弊处了:

通常来说,延迟渲染管线都需要一组称之为G-Buffer的Render Target,这些贴图内存储了一切光照计算需要的材质信息。当今的3A游戏中,材质种类往往复杂多变,需要存储的G-Buffer信息也在逐年增加。相同分辨率的情况下,由于材质复杂度和逼真度的提升,G-Buffer需要的带宽足足提高了一倍,这还不考虑逐年提高的游戏分辨率的因素。

对于overdraw较高的场景,G-Buffer的绘制产生的读写带宽往往会成为性能瓶颈。于是学界提出了Visibility Buffer的新渲染管线。基于Visibility Buffer的算法不再单独产生臃肿的G-Buffer,而是以带宽开销更低的Visibility Buffer作为替代

比较其两者的渲染管线区别可见下图所示:

其中Visibility Buffer管线包含了三个阶段:

  • Visibility Passes: 对场景进行光栅化,将Primitive ID和Instance ID(或Material ID)保存到ID Texture里(顺手做个Depth Prepass),也就是说只有可见的Primitive才会进入后续的阶段;

  • Worklist Pass:构建并Worklist,这一步是为了驱动下一步,将屏幕划分成很多tile,根据使用到某个Material ID的tile加到该Material ID的Worklist里,作为下一步的索引;

  • Shading Passes : 使用Compute Shader对每个Material ID进行软光栅,获取顶点属性并插值,然后再进行表面着色。

接下来我们来看一个以输出一个1080P图像为例不同buffer下的大小:


从上图中可以明显看出两种buffer对内存的消耗情况。

而UE5 Nanite 的一大特色是Visibility Buffer。它是一个 R32G32_UINT 纹理,包含每个像素的三角形和深度信息。此时不存在材料信息,具体可视化标识如下:

一般Visibility Buffer通常需要这些信息:

  • (1)InstanceID,表示当前像素属于哪个Instance(16~24 bits);

  • (2)PrimitiveID,表示当前像素属于Instance的哪个三角形(8~16 bits);

  • (3)Barycentric Coord,代表当前像素位于三角形内的位置,用重心坐标表示(16 bits);

  • (4)Depth Buffer,代表当前像素的深度(16~24 bits);

  • (5)MaterialID,表示当前像素属于哪个材质(8~16 bits);

以上,我们只需要存储大约8~12 Bytes/Pixel即可表示场景中所有几何体的材质信息,同时,我们需要维护一个全局的顶点数据和材质贴图表,表中存储了当前帧所有几何体的顶点数据,以及材质参数和贴图。

在光照着色阶段,只需要根据InstanceID和PrimitiveID从全局的Vertex Buffer中索引到相关三角形的信息;进一步地,根据像该素的重心坐标,对Vertex Buffer内的顶点信息(UV,Tangent Space等)进行插值得到逐像素信息;再进一步地,根据MaterialID去索引相关的材质信息,执行贴图采样等操作,并输入到光照计算环节最终完成着色,有时这类方法也被称为Deferred Texturing。

直观地看,Visibility Buffer减少了着色所需要信息的储存带宽(G-Buffer -> Visibility Buffer);此外,它将光照计算相关的几何信息和贴图信息读取延迟到了着色阶段,于是那些屏幕不可见的像素不必再读取这些数据,而是只需要读取顶点位置即可。基于这两个原因,Visibility Buffer在分辨率较高的复杂场景下,带宽开销相比传统G-Buffer大大降低。但同时维护全局的几何、材质数据,增加了引擎设计的复杂度,同时也降低了材质系统的灵活度,有时候还需要借助Bindless Texture等尚未全硬件平台支持的Graphics API,不利于兼容。

二、Deferred Material

我们都知道实际项目场景中会有大量材质,这些材质可能对应不同的 Shader,即使 Shader 相同还有可能会引用不同的 Textures。Visibility Buffer 本质上属于 Deferred Shading,而 Deferred Shading 最大的缺陷就是难以处理不同材质\Shader。为了解决这个问题,业界也提出了一些解决方案,其中比较有效的是 Deferred Material(也有称 Deferred Texture)

Deferred Material 的核心思想是将材质分类,找出每个材质对应的像素进行 Shading。因此需要将 Visibility Buffer 像素按材质分类处理。

其实主要就是在屏幕上渲染Material Id这种信息,然后统一的在screenspace把material data读出来用,之后可以自行做shading或者填充gbuffer。

可以来直观的看一下UE5 Nanite 生成的Material ID Buffer,它并未存储在一张UINT类型的贴图,而是将UINT类型的Material ID转为float存储在一张格式为D32S8的Depth/Stencil Target上,理论上最多支持2^32种材质(实际上只有14 bits用于存储Material ID),而Nanite Mask会被写入Stencil Buffer中。

由于是屏幕空间处理,最直接的想法是每个材质绘制一个全屏 Quad,找出匹配的像素进行 Shading。如果场景中材质数量为 N,屏幕像素为 M,总计需要 NxM 次的计算开销,如果场景中有大量的材质,这样的性能将无法接受。可见UE5 Demo中山谷的材质复杂的如下图:

另一种比较容易想到的方案是只处理每个材质对应的像素,最终每像素只会计算一次。但是现实很骨感,在实现上为了并行处理,必须用到原子操作,而原子所带来的同步开销,最终会抵消掉这个优化,另外还需要保证数据连续存储,否则无法实现一个 Indirect Dispatch 处理同材质的所有像素,中间要经过几个 Compute Pass 来回倒腾数据,最终所有这些加起来总开销更大,显然这也不是最佳的解决方案。

2.1 Material Culling

UE5 借鉴了几何处理的方法,很巧妙地来解决这个问题,称之为 Material Culling 。Visibility 像素经过 Material Culling 之后,实现了每像素只计算一次的效果,并且没有太多额外的性能和内存开销,总体上达到了很好的性能平衡。

同几何处理类似,Material Culling 分为基于 Tile 的粗剔除和基于 Pixel 的细剔除 2 种剔除粒度。从宏观流程上来看,先根据 Visibility Buffer 生成 Material Depth,接下来将屏幕划分 Tile,记录每个材质对应的 Tile 列表,最后每材质绘制对应数量的 Tile,并使用 Material Depth 剔除无效像素,只对有效像素 Shading,从而提升了整体性能。

Nanite在Base Pass绘制阶段并不是每种材质一个全屏Pass,而是将屏幕空间分成若干8x8的块,比如屏幕大小为800x600,则每种材质绘制时生成100x75个块,每块对应屏幕位置。为了能够整块地剔除,在Emit Targets之后,Nanite会启动一个CS用于统计每个块内包含的Material ID的种类。由于Material ID对应的Depth值预先是经过排序的,所以这个CS会统计每个8x8的块内Material Depth的最大最小值作为Material ID Range存储在一张R32G32_UINT的贴图中:

有了这张图之后,每种材质在其VS阶段,都会根据自身块的位置去采样这张贴图对应位置的Material ID Range,若当前材质的Material ID处于Range内,则继续执行材质的PS;否则表示当前块内没有像素使用该材质,则整块可以剔除,此时只需将VS的顶点位置设置为NaN,GPU就会将对应的三角形剔除。由于通常一个块内的材质种类不会太多,这种方法可以有效地减少不必要的overdraw。实际上通过分块分类减少材质分支,进而简化渲染逻辑的思路也并非第一次被提出,比如《Uncharted 4》在实现他们的延迟光照时,由于材质包含多种Shading Model,为了避免每种Shading Model启动一个单独的全屏CS,他们也将屏幕分块(16x16),并统计了块内Shading Model的种类,根据块内Shading Model的Range给每个块单独启动一个CS,取Range内对应的Lighting Shader,以此避免多遍全屏Pass或者一个包含大量分支逻辑的Uber Shader,从而大幅度提高了延迟光照的性能。

在完成了逐块的剔除后,Material Depth Buffer就派上了用场。在Base Pass PS阶段,Material Depth Buffer被设置为Depth/Stencil Target,同时Depth/Stencil Test被打开,Compare Function设置为Equal。只有当前像素的Material ID和待绘制的材质ID相同(Depth Test Pass)且该像素为Nanite Mesh(Stencil Test Pass)时才会真正执行PS,于是借助硬件的Early Z/Stencil我们完成了逐像素的材质ID剔除。

UE_Visibility Buffer Deferred Material相关推荐

  1. 批量 材质 调整_寒霜引擎的PBR实践3.0(一)材质篇

    随着对游戏美术品质要求的提高,对整个开发从流程pipeline到从业人员的素质要求都开始增加.传统的美术工作流因为依赖个人的经验和感觉,所以在制作大体量项目的过程中很难达到理想效果.而这时,由项目组内 ...

  2. POC of settype read harmonization - using description settype as example

    Background introduction In CRM WebUI, Genil layer will call CRM PRODUCT API CRM_PRODUCT_GETDETAIL_AP ...

  3. Game Programming with DirectX -- 08[Mesh]

    GameProgrammingwithDirectX--08[Mesh] 第八集 Mesh 构造简单的球的3D模型已经复杂化了, 如果用代码构造比球更复杂的3D模型就更难了, 还好有专业级的3D模型设 ...

  4. Deferred Shading,延迟渲染(提高渲染效率,减少多余光照计算)

    Deferred Shading,看过<Gems2> 的应该都了解了.最近很火的星际2就是使用了Deferred Shading. 原帖位置:    http://blog.csdn.ne ...

  5. UE4 Material Properties

    材质 & 贴图 On this page: Physical Material Material Translucency Translucency Self Shadowing Usage ...

  6. Deferred Shading,延迟渲染(提高渲染效率,减少多余光照计算)【转】

    Deferred Shading,看过<Gems2> 的应该都了解了.最近很火的星际2就是使用了Deferred Shading. 原帖位置:    http://blog.csdn.ne ...

  7. Deferred Shading

    Deferred Shading 到目前为止,本书中所使用的渲染方法都是forward rendering,本质上来说是指独立渲染场景中的每一个objects,对于每一个object都要使用light ...

  8. Forward vs Deferred vs Forward+ Rendering with DirectX 11

    原文:http://www.3dgep.com/forward-plus/ Introduction Forward rendering works by rasterizing each geome ...

  9. Page cache和Buffer cache[转1]

    http://www.cnblogs.com/mydomain/archive/2013/02/24/2924707.html Page cache实际上是针对文件系统的,是文件的缓存,在文件层面上的 ...

最新文章

  1. 你用iPhone打游戏,有人却用iPhone来训练神经网络
  2. java数组的clone方法_深入浅出,如何更彻底地理解Java数组的clone方法
  3. python如何爬虫股票数据_简单爬虫:东方财富网股票数据爬取(python_017)
  4. boost::allocator_destroy的实例
  5. pandas所占内存释放
  6. corba的兴衰_数据科学薪酬的兴衰
  7. 从企业发展的角度来分析做网站的重要性
  8. matlab极坐标下的二维傅里叶变换_形象理解二维傅里叶变换
  9. 如鹏网学习笔记(十二)HTML5
  10. python中delta是什么意思_python – 根据dataframe中的值计算delta
  11. 海思Hi3798处理器参数,Hi3798芯片详细信息介绍
  12. Ubuntu学习笔记(2)---安装LumaQQ
  13. 【​观察】加速“云矩阵”生态落地,京东云能否成为新变量?
  14. 页面无法自动播放音频的解决方案
  15. oracle 电子书大全
  16. 你以为打上马赛克就安全了吗?用Python一键还原,了解一下?
  17. 深入理解浏览器兼容性模式
  18. 个人博客处理——页面处理
  19. 张良、岳飞、钢铁是怎样炼成的
  20. c++/qt/opencv实现魔方复原【机器人应用】

热门文章

  1. 前端js华为云obs上传下载文件与进度条的设置
  2. 好书推荐:《黑客秘笈:渗透测试实用指南》
  3. c/c++实现五子棋
  4. 无需修改代码,用 fcapp.run 运行你的 REST 应用
  5. aria- 标签 html,HTML5教程 如何使用ARIA
  6. 谷歌优化排名怎么做?Google SEO怎么优化?
  7. TIOBE 2月榜单出炉:排行榜前8位在过去七年中排名没有变化,是否意味着编程语言没有变化?
  8. web cracker
  9. 拆书笔记27|孔夫子和机器人
  10. 作为Gatekeeper,请守好交付质量关口(Quality Gate)