引言
延迟渲染提供了多种对性能影响适中的动态光照、更多的后期处理器,以及更好的整体环境观感。
什么是延迟渲染?
延迟渲染系统在初始通路中使用多重渲染目标(MRT)来设置一组所谓的几何体缓存(G-Buffers)。 在这一初始通路中,有关每个像素的位置、法线、散射色、高光色等信息被储存在四个独立的渲染目标中。 之后,这一信息被用于计算动态照明、阴影和所有特殊效果,即后期处理特效。 因为这些效果的所有所需信息都存放在几何体缓存中,所以不需要为这些通路重新渲染几何体。 相反,在延迟渲染中,每个特效都只需一个全屏四边形。
支持延迟渲染的系统包括电脑(包含 SM3.0 及更高版本的 DX9、DX10、DX11(向下兼容 SM4.0))、PlayStation 3 和 Xbox 360。 其配置可以完全在 vForge 中进行,但依然允许在运行库中修改参数等选项。
在延迟渲染着色中,最终成像由后期处理特效生成,该步骤将之前生成的几何体缓存(G-Buffers)作为输入使用
注:正向和延迟渲染系统都是 Vision 引擎插件的一部分,以完整源代码的方式提供。 提供源代码是为了方便用户自定义。
如何激活延迟渲染
在主层下拉菜单中选择相应的渲染器,即可将场景设为延迟着色模式:
各种渲染器节点类可能有自身独有的渲染设置。 选中节点后,这些设置会显示在主层属性中:
特性
延迟渲染系统的大部分渲染特性都作为后期处理实施。
一些特效在正向渲染系统中不可用。 延迟渲染系统特有的一些其他特性包括:
大量的动态光照
延迟渲染的最大优势之一是可以轻易渲染大量完全动态光照,因为归根到底,渲染一个光源就相当于用合适的像素着色器渲染一个球体、锥体或全屏四边形。 只要不需要动态阴影,哪怕中等配置的电脑也往往能渲染数十个光源,且保持高帧率。
和正向渲染下一样,延迟渲染下,动态光源可以附加阴影贴图组件。 但在这种情况下,逐光源负担仅略低于正向渲染模式。
Vision 延迟渲染系统还支持光照贴图和光照网格等预计算照明,它们在 vLux 中生成,被一同用于延迟光照。
如启用该功能,则延迟渲染中不会考虑静态光源。 相对地,在初始 G-Buffer 通路中所采用的是光照贴图和光照网格。 随后,只有动态光源才会被延迟光照纳入计算。
多重采样
在 DirectX11 中,Vision 引擎的延迟渲染模式支持硬件 MSAA。 因为多重采样的深度信息没有太大意义,所以引擎在渲染时会采取变通手段,先确定三角形边缘,然后单独处理这些边缘,与几何体分开处理。 请注意,这种抗锯齿形式仅对三角形边缘和 alpha 测试几何体(支持 alpha-to-coverage(alpha 覆盖))有效。
注:只有包含 Shader Model 5.0 的 DirectX11 支持延迟渲染硬件 MSAA。
在所有平台上,Vision 延迟渲染系统都提供 FXAA 后期处理器,可用来模拟”真实”的抗锯齿。
注:一般来说,我们推荐使用 FXAA 而非硬件 MSAA,因为其性能表现通常较好,同时质量也依然不错。
光照技术
延迟渲染系统的一大长处是能够处理大量动态光照。 所以,为所有场景光照使用动态光照也许会是一个诱人的选择。 但在某些情况下,将静态光照烘培入光照贴图和光照网格依然是值得推荐的做法。 这不仅是为了性能,也是为了光照质量。
光照贴图
如果启用光照贴图,它会建议渲染系统将光照贴图 / 光照网格色作为基底光照使用,而非从黑色场景开始渲染。 动态光照被渲染并叠加到该基底光照之上。
注:这一选项同时影响光照贴图和光照网格的光照。
半透明
在任何延迟型渲染系统中,透明度都是一大难题。 问题在于,按定义,延迟光照是作为后期处理步骤添加到已渲染的场景中的。 如果场景在应用光照之前已有透明度多边形,则延迟光照会覆盖到其后面的多边形,这些透明度多边形将显示为无光照。
因此,半透明必须在光照之后渲染。
以下为延迟渲染的渲染流水线:
无透明度材质: 包括简单 alpha 测试和无透明度的材质
光照后期处理: 所有已经被渲染的几何体获得光照
透明度几何体: 包括粒子和其他特效(在这一批次中渲染的几何体仍然无光照)
延迟渲染系统会照顾到每一个半透明的材质,让它们都得到一个合适的着色器,该着色器是”后期光照”渲染批次的一部分。
延迟渲染中的粒子
粒子本质上是半透明的。 粒子编辑器允许设置一个渲染器钩子常数,以此判定粒子是在延迟光照步骤、还是在后期光照步骤中渲染。
注:默认设置对任何半透明粒子都是准确的。 所以粒子特效可以在正向和延迟渲染中无缝切换使用。
如果粒子需要设为不透明(或仅 alpha 测试)并纳入延迟光照通路,则情况会更为复杂。 这不仅要更改渲染器钩子常数,还要一个专用着色器来正确填充所有 G-Buffer 信息。
半透明的低分辨率渲染
在主机上,Vision 引擎支持以比主渲染分辨率更低的分辨率对特定半透明进行渲染,例如叠加粒子和 alpha 混合粒子。
这会略微降低高频细节的视觉品质,但具有极大的性能优势,特别是对有很多大型粒子特效的场景而言。
使用延迟渲染的最佳场合
以下情况最好使用延迟渲染
大型开放式游戏世界(MMO)
光照贴图技术不适合大型游戏世界,因为需要一定光照质量的光照贴图量实在太大。 因此,这类场景应完全使用延迟模式动态照明。
通常,场景中会有一个指向性日光(受时间变化系统控制),另加火炬光、提灯等本地光源。
室内场景
延迟着色可能也是适合室内场景的光照解决方案。 在这种情况下,光照贴图是一个不错的方案,可将静态光照连同辐射度一起烘培入光照贴图。 动态光照被渲染在顶层,可用于动态火炬光、摇曳的光照和其他特效。
不应使用延迟着色的场合
1、要求带有辐射度的高品质预计算光照的场景。
2、
高度依赖半透明(而非简单的 alpha 测试透明)的场景。
3、硬件要求很低(因为延迟渲染需要 Shader Model 3.0 或更高版本,以及运算速度较快的显卡)的游戏 / 应用程序。
延迟渲染系统组件
延迟渲染系统是 Vision 引擎插件的一部分,可在以下文件夹找到
<VisionSDK>/Source/Vision/Runtime/EnginePlugins/VisionEnginePlugin/Rendering/DeferredShading。 其主要类为:
VDeferredRenderingSystem: 该类包含延迟渲染系统的所有初始化功能。 此类负责创建各种渲染目标和后期处理特效,以及管理太阳、雾和天空设置。 每一种后期处理特效都通过该类访问。
DeferredShaderProvider_cl: 提供延迟渲染初始通路的着色器。 这一延迟着色器的提供器负责为延迟着色 G-Buffer 通路(”初始通路”)自动生成和分配着色技术。 它会考虑材质属性,且仅处理带有自动着色器分配的材质。
DeferredShadingRenderLoop_cl: 此类负责渲染场景的初始 G-Buffer 通路,从而生成此后的后期处理特效所需的缓存。
TranslucencyRenderLoop_cl: 由于延迟渲染的局限,必须在应用大部分后期处理特效之后、在一个独立通路中渲染半透明。 此类负责渲染这些半透明对象。
延迟渲染系统中使用的着色器
着色器目标配置和世界空间位置重构
为了将所有需要的信息填入四个可用的渲染器目标,G-Buffer 通路以一种特殊的方式对数据进行编码。 您可以查看 DeferredShadingHelpers.inc 文件,了解提取数据的结构和打包 / 解包编码。
初始通路的渲染器目标设置如下:
Render Target 0(渲染器目标 0): RGBA 累积缓存。 如使用光照贴图 / 光照网格,应用光照贴图 / 光照网格的结果就储存在这里。 如没有使用光照贴图 / 光照网格,初始通路会将此项(基底纹理 * 环境光)存入该缓存。 请注意,在后期处理中,累积缓存会不断更新,因为(正如其名)最终图像结果在该缓存中累积。
Render Target 1(渲染器目标 1): RGB: 散射色。 Alpha: 高光指数。
Render Target 2(渲染器目标 2): RGB: 法线矢量。 Alpha: 当前未使用。 可用来存储材质索引等。
Render Target 3(渲染器目标 3): 在电脑和 Xbox 360 上是一个双组分(RG)16 位浮点缓存。 R 存储深度信息,G 存储高光色。 高光色以单个 16 位浮点值编码。 在 PlayStation 3 上是一个 RGBA8 缓存,RG 存储深度信息,BA 存储高光色。
应用后期处理特效时,该深度信息足以重构每个像素在世界空间中的确切位置。 需要像素位置的后期处理特效(例如 SSAO)使用
VRendererNodeHelper::GetFrustumMeshBuffer 生成的特殊顶点缓存来渲染。 视平截体的远端角存储在一组该几何网格缓存的平面化映射顶点中。 因为像素在屏幕上的位置和在视平截体中的深度均为已知,所以精确的像素位置可以通过一次乘法和一次加法计算出来。
实施供延迟着色环境使用的自定义着色器
延迟渲染所使用的着色器可通过 vForge 创建,方法和您为正向渲染系统创建着色器完全一样。 可视化着色器编辑器支持一种用于延迟渲染的特殊输出块,可用来便利地创建初始通路(G-Buffer)着色器。
在延迟渲染系统中有三种不同类型的基本着色器。
用于初始 G-Buffer 通路的着色器。
这些着色器需要向初始通路渲染环境下的四个渲染器目标输出位置(深度)、法线、散射色和高光色 / 指数信息。 以下情况有理由为 G-Buffer 通路创建自定义着色器:
您想用不同的值初始化累积缓存 - 例如,您有一种应用静态或环境照明的不同手段。
您想改变部分 G-Buffer 信息的计算方式(例如,在存入 G-Buffer 之前搅动法线或添加深度补偿)。
您想为延迟渲染系统添加自定义功能(例如,为动态模糊存储动态矢量,或是存储材质索引)
存储在着色器库 DeferredShading.ShaderLib 中的着色器是初始 G-Buffer 通路的默认着色器。 如果您在 vForge 着色器分配编辑器中将材质着色器分配类型设为”automatic(自动)”,则将使用这些着色器。 如果您想使用自定义 G-Buffer 着色器,您需要将分配类型设为”manual(手动)”。
用于半透明 / 非延迟渲染操作的着色器
如前文所解释,粒子、玻璃等半透明场景元素无法被延迟渲染系统正确地处理。 因此,它们需要在一个专用的半透明渲染通路内单独渲染,由 TranslucencyRenderLoop_cl 进行处理。 这一渲染循环的执行排序在渲染流程中相对靠后(在 SSAO、雾、光照等步骤之后,但在泛光、抗锯齿、景深和色调映射之前),因此不受大部分延迟渲染操作的影响。
另外,如果您想让任何非透明度几何体在渲染时不产生延迟渲染特效,您只需将该几何体放入半透明渲染循环即可。
半透明渲染循环中的粒子和体积锥体等特效会被自动渲染,而实体、静态几何体实例和几何网格缓存对象则需要应用专门的着色器。 这些着色器的编写方式和在正向渲染中一样(不应输出 G-Buffer 信息,而是仅输出单个 RGBA 值),但必须在 vForge 着色器编辑器中将相应的着色技术标为”post-basepass”(后期基底通路)。 这么做可以确保分配给该技术的材质被半透明渲染循环渲染。
用于后期处理特效的着色器
取决于想要的后期处理效果,用于后期处理特效的着色器可以差别很大。 通常情况下,需要一个或多个 G-Buffer 渲染器目标作为输入,以及累积缓存作为输出。 熟悉如何创建自定义后期处理器的最好方法是在延迟渲染系统中研究现有后期处理特效的实现机制。
性能考量和优化提示
延迟渲染,尤其是结合仿立体光照贴图和光照网格的静态照明时,会对 GPU 造成很大的负担。 延迟渲染应用的瓶颈通常为 GPU,特别是高分辨率下,工作负载最高的通常是像素着色单元和 GPU 内存带宽。
当然,很多与正向渲染相关的优化技术也适用于延迟渲染。 例如,我们同样推荐尽量把多边形计数保持在低水平,材质也应尽可能简单。 下文列出了一些延迟渲染特有的优化建议。
针对具体平台的延迟渲染系统性能优化
因为延迟渲染是一种相当复杂的技术,需要大量 GPU 着色器性能和内存带宽,所以在所有平台上都要为确保良好的性能而特别留意。 在主机上,可使用多种平台专用的优化技巧来提升性能。 此外,应把以下平台间差异纳入考量:
多重渲染目标(G-Buffer 通路): 在 PlayStation 3 上,多重渲染目标(MRT)要求所有像素着色器渲染到一个 MRT 上下文输出,输出的颜色值与在着色器渲染目标输出蒙版中启用的数量相同。 例如,如果输出蒙版设定了所有位元,就不能让像素着色器只写入一个颜色值。 PlayStation 3 的延迟渲染模式不支持 HDR。
景深: Xbox 360 仅支持四分之一渲染。 一般来说,我们本来就建议使用四分之一渲染,因为它具有明显的性能优势,而视觉品质的损失也相对较低。
半透明渲染循环: 仅 PlayStation 3 和 Xbox 360 支持对四分之一渲染目标进行叠加和阿尔法混合半透明渲染。 如果您使用大量半透明对象,例如大量雾粒子,这可以极大改善性能。
使用标准延迟渲染流水线的优化
在未修改版的 Vision 延迟渲染系统中,您可以专注于禁用不需要的功能,使场景适合延迟渲染的特性:
禁用一切不是必须的后期处理特效。 例如,您可以考虑仅启用景深来实现游戏内场景切换。
在一切支持低分辨率缓存的场合为后期处理特效使用低分辨率缓存(例如主机上的景深、SSAO、半透明渲染)。 这有时可以极大提升性能,而视觉品质的损失并不大。
很多后期处理器允许设置相应特效的质量或细节层次。 例如,您可以在不同的阴影贴图技术(如 PFC4、PCF16、PCSS16)中选择,或设定屏幕空间环境遮挡的样本数量。 在电脑上,您应允许用户在不同质量设置之间切换。
对于动态光照(如果不使用光照贴图 / 光照网格光照,则也包括静态光照),其性能影响主要取决于光照的范围。 全局光照或大半径聚光灯需要渲染更大的球体或锥体,从而增加受影响像素的数量。 平行光照的半径与性能影响无关,因为总是作为全屏四边形渲染。
如果您需要很多光源,请考虑把预计算照明和(延迟)动态光照混合使用。 因为 Vision 引擎允许这些技术的组合使用,您可以将静态光照信息编码到光照贴图中,同时依然为动态光照使用延迟渲染。
使用自定义延迟渲染功能时的优化
使用自定义延迟渲染特效时,编写快速着色器、最小化 CPU 占用这两条基本的优化规则完全适用。 此外,您应考虑到以下几点:
如果您的后期处理特效需要临时划痕纹理,请使用
ScratchTexturePool_cl::GetScratchTexture 辅助函数获取可以和其他后期处理器共享的纹理(如果纹理属性匹配)。
为使用深度信息的后期处理特效编写像素着色器时,在着色器开头使用一条 clip 指令(在抓取深度值之后)以确保天空被高效地遮蔽,不参与此后的计算。 但是,这一技巧仅在天空覆盖屏幕大量范围的情况下值得使用。 在室内环境下,放弃 clip() 指令更佳。
强烈推荐在 PIX、GPAD 或类似的平台专用剖析工具中分析自定义着色器,因为这是确认瓶颈并改善性能的最佳手段。