Unity 渲染管线

文章目录

  • 1. 渲染管线
    • 1.1 选择一种渲染管线
  • 2. 默认管线 Built-in Render Pipeline
    • 2.1 渲染通路 Rendering paths
        • 2.1.0.1 前向渲染 Forward Rendering
        • 2.1.0.2 延迟着色 Deferred Shading
        • 2.1.0.3 老版本的延迟着色 Legacy Deferred
        • 2.1.0.4 老版本的顶点着色 Legacy Vertex Lit
        • 2.1.0.5 渲染通路比较
      • 2.1.1 前向渲染 Forward rendering path
        • 2.1.1.1 实现细节
        • 2.1.1.2 Base Pass
        • 2.1.1.3 Additional Pass
        • 2.1.1.4 效率 Performance Considerations
      • 2.1.2 延迟着色 Deferred Shading rendering path
        • 2.1.2.1 概述
        • 2.1.2.2 需求
        • 2.1.2.3 效率
        • 2.1.2.4 实现细节
          • G-Buffer Pass
          • Lighting Pass
      • 2.1.3 老版延迟着色 Legacy Deferred rendering path
        • 2.1.3.1 概述
        • 2.1.3.2 需求
        • 2.1.3.3 效率
        • 2.1.3.4 实现细节
          • Base Pass
          • Lighting Pass
          • Final Pass
      • 2.1.4 顶点光照渲染 Vertex Lit Rendering Path
    • 2.2 内建渲染管线的硬件需求Hardware Requirements for the Built-in Render Pipeline
      • 2.2.1 概要
      • 2.2.2实时阴影 Realtime Shadows
      • 2.2.3 后处理效果 Post-processing Effects
      • 2.2.4 Shaders
  • 3. 通用渲染管线 Universal Render Pipeline
  • 4. 高级渲染管线 High Definition Render Pipeline
  • 5. 可编程渲染管线 Scriptable Render Pipeline
    • 5.1 SRP Batcher 合批
      • 5.1.1开启SRP合批
      • 5.1.2 各个平台,支持合批的Unity版本:
      • 5.1.3 SRP Batcher 工作原理
      • 5.1.4 SRP Batcher 的兼容性
      • 5.1.5 SRP Batcher 性能剖析
        • 显示信息
      • 5.1.6 Unity Frame Debugger 中的SRP Batcher 数据

1. 渲染管线

Unity中,有多种不同的渲染管线可以选择。简单的说,渲染管线负责从场景中选择物体并渲染到屏幕上,包括三个基本步骤:

  • 裁剪
  • 渲染
  • 后效

不同的渲染管线有不同的功能和性能特性,分别适应于不同的游戏,应用和平台。

将项目从一个渲染管线切换到另一个很困难,因为不同渲染管线使用不同的shader进行渲染,而且拥有不同的特性。

1.1 选择一种渲染管线

Unity提供了下面的渲染管线:

  • Built-in Render Pipeline 默认渲染管线,是一个通用渲染管线,相对固定,仅可以有限度的进行控制。
  • Universal Render Pipeline (URP) 是可编程渲染管线,可以方便地进行自定义渲染,针对不同的平台进行优化。
  • High Definition Render Pipeline (HDRP) 也是一种可编程渲染管线,可以在高端平台上创建技术领先的,高逼真度的图形渲染。
  • Scriptable Render Pipeline (SRP) 可以基于可编程渲染管线API,创建自定义的渲染管线。你可以从头开始创建一个全新的渲染管线,也可以通过修改URP和HDRP来适应自己的项目。

2. 默认管线 Built-in Render Pipeline

默认渲染管线相对于可编程管线,自定义扩展性不高,仅可以通过选择不同的 rendering paths ,并通过command buffers 和callbacks 对其功能进行扩展。

2.1 渲染通路 Rendering paths

默认管线定义了不同的渲染通路,一个渲染通路是一系列光照和阴影处理。不同的渲染通路的功能和性质特性都不一样,要根据项目特性和目标平台选择合适的渲染通路。

可以在ProjectSettings>Graphics>Tier Settings种设置不同渲染质量的渲染通路,该设置也可以在摄像机中被覆盖掉。

如果设置的渲染通路,在硬件设备上不支持,Unity会自动选择低一级的渲染通路。例如,对于不支持延迟着色的硬件,会使用前向渲染。

2.1.0.1 前向渲染 Forward Rendering

前向渲染,是默认管线中的默认通路。

在前向渲染中,实时光照效率非常低。可以通过指定每个像素可以接受的光源数量来调整负载。对场景中其它的光源则会使用逐顶点,或逐对象的高效率,低效果的方式。

如果你的场景中没有很多的实时光源,或者光照效果没有那么重要,那么选择前向渲染。

2.1.0.2 延迟着色 Deferred Shading

延迟着色可以实现更好的光照和阴影渲染效果。

延迟着色需要GPU的支持,并且有一些限制。不支持半透明物体(这类物体会使用前向渲染),正交投影(这类摄像机使用前向渲染),以及硬件抗锯齿(尽管可以通过后效来实现类似效果)。同时,延迟着色对culling mask不完全支持,而且对 Renderer.receiveShadows 标识按照true(设置无效)来处理。

如果你的场景有很多的实时光源,需要很高的渲染真实度,并且目标平台硬件支持,则使用延迟着色。

2.1.0.3 老版本的延迟着色 Legacy Deferred

Legacy Deferred(light prepass)与Deferred Shading 类似,只是使用了不同的技术,不支持新的PBR standard shader。

2.1.0.4 老版本的顶点着色 Legacy Vertex Lit

Legacy Vertex Lit 实现低级效果并且不支持实时阴影,是前向渲染的一部分。基于顶点的光照。

2.1.0.5 渲染通路比较

Deferred Forward Legacy Deferred Vertex Lit
Features特性
逐像素光照(normal maps, light cookies) Yes Yes Yes No
实时阴影 Yes 会有警告?With caveats Yes No
反射探针 Yes Yes No No
深度/法线缓冲区 Yes 需要额外的渲染 No No
软边粒子 Yes
半透明对象 No Yes No Yes
抗锯齿 No Yes No Yes
光照裁剪掩码 部分支持 Yes 部分支持
光照渲染精度 逐像素 部分逐像素 逐像素 逐顶点
性能
逐像素光照消耗 由照亮的像素数决定 照亮的像素数*照亮的对象数 由照亮的像素数决定
对象的平均渲染次数 1 每像素受光照数 2 1
相对简单场景的消耗 中等
平台支持
PC(Windows/Mac) Shader Model 3.0+ & MRT(MultiRenderTarget) All Shader Model 3.0+ All
Mobile (iOS/Android) OpenGL ES 3.0 & MRT, Metal (on devices with A8 or later SoC) All OpenGL ES 2.0 All
Consoles XB1, PS4 All XB1, PS4, 360 All

2.1.1 前向渲染 Forward rendering path

前向渲染根据照亮对象的光源的数量,对对象进行一次或多次渲染。光源本身根据其强度等设置,会被进行不同的处理。

2.1.1.1 实现细节

前向渲染中,照亮物体,并且一定数量的亮度最高的光源,按照逐像素光照来进行渲染。最多由4个光源按照顶点光照进行渲染。其它的光源,按照球函数(一种特定算法,SH:Spherical Harmonics )计算光照,速度很快但仅仅是一种近似模拟。光源是否被逐像素处理,取决于:

  • 光源的 RenderMode 被设置成 Not Important 的,会被处理成顶点光或球函数模拟光。
  • 最亮的方向光,一定是逐像素光照。
  • Render Mode = Important 的光源是逐像素的。
  • 如果最终对象的逐像素光源数量小于QualitySetting.PixelLightCount设置的值,则会选择更多的光源为逐像素光照,来降低亮度。

对每个对象的渲染,会执行下面的步骤:

  • Base Pass 来渲染一个逐像素的方向光和所有的逐顶点/SH的光照。
  • 对每个逐像素的光照,额外进行一次渲染。

例如,对象被一些光源照亮,如下图的圆圈代表的对象,被A-H的8个光源照亮。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVNLRbxQ-1581473824688)(https://docs.unity3d.com/uploads/Main/ForwardLightsExample.png)]

这里假定所有的光源,拥有想通过的设置,颜色,强度,默认的RenderMode,因此它们对该对象的影响按照A-H的顺序。最亮的光源按照逐像素渲染(A-D,假定设置为每个对象4个逐像素光源),最多4个光源为逐顶点光照(D-G),剩下的用SH渲染(G-H):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ASE271PA-1581473824689)(https://docs.unity3d.com/uploads/Main/ForwardLightsClassify.png)]

注意到光照组之间是重叠的,例如最后一个逐像素的光源同时被混合进了逐顶点光源中,以避免光源或对象的移动导致光照的跳变。

2.1.1.2 Base Pass

Base Pass 为每个对象渲染一个逐像素的方向光,所有的逐顶点光源和SH光源。该过程同时会在Shader中处理光照贴图(lightmaps),环境光,自发光。该过程中处理的方向光可以渲染阴影。注意,LightMaps对象不会再渲染SH光照。

注意,如果Shader中设置了 “Only Directional Light” pass 标识,则该Pass仅渲染主方向光,环境/光照探针和光照贴图(SH和逐顶点光照将不在该Pass中渲染)。

2.1.1.3 Additional Pass

Additional Pass 负责渲染影响到对象的一盏光源的逐像素光照。这些pass不会渲染阴影(也就是说前向渲染,仅支持一个带阴影的方向光),除非添加了multi_compile_fwdadd_fullshadows 声明。

2.1.1.4 效率 Performance Considerations

Spherical Harmonics光照拥有非常快的效率,CPU占用很小,GPU负载也非常低(base pass总是会计算SH;同时由于其工作原理,SH光源数量的增加,几乎不会增加性能损失)。(SH没有深入研究,所以先按照字面意思解释这部分吧)

SH光照的缺点:

  • 它们是逐顶点计算的,所以不支持light cookies(简单理解为镂空的灯笼效果,应一张贴图部分遮挡光源)和lightmaps。
  • SH光照将以一个比较低的频率执行。SH光照不会产生强烈的光照过度,它们只会影响漫反射(diffuse)光照(对高光来说频率太低了)。
  • SH光照算法,在光源离表面很近时,渲染效果可能不太正常。

(译者)以前自研引擎也曾经实现过一种效率很高,但是效果不太好的点光源:

  • 在CPU中计算光照位置,颜色,强度,衰减。这些可以根据参数实时变化,模拟摇曳的灯光等效果。
  • 渲染一个物体时,将照亮该物体的该类光源的参数传给shader
  • 在vertex shader中,按照顶尖距离这些光源的距离,和光源自身的参数,计算该顶点受这些光源的影响,并混合,叠加到顶点颜色中(或者其它的VertexOutput的字段)
  • 在pixel shader中,处理diffuse时混合上面计算的颜色。

感觉跟SH有点像,基于球形的衰减照亮顶点。

2.1.2 延迟着色 Deferred Shading rendering path

延迟着色的技术原理,可以参考Wikipedia: deferred shading

2.1.2.1 概述

使用延迟着色时,每个对象能够接收的点光源数量没有限制,所有的光源都将按照逐像素渲染,意味着都能跟光照贴图正确地配合,以及支持cookies和阴影。

延迟着色地优势是负载跟照亮一个像素的光源数量是成比例的。该负载只跟光源在场景中的体积(照亮范围)有关,而跟它照亮的对象数量无关。因此提升效率的方法之一,就是让光源的照亮范围小一些。延迟着色具有一致性,可以按照我们预想的进行渲染。每个光源都是逐像素的,因此再比较大的三角形上形成颜色块(基于顶点的光照因为只计算了顶点颜色,并再像素阶段进行插值而导致一些色阶)。

延迟着色的缺点是不支持抗锯齿和半透明物体(这些物体将在前向渲染中处理)。同时不支持对特定对象是否接受阴影的设置,并且culling mask也仅是有限度的支持:最多支持4个culling masks,也就是说culling layer mask 至少要包含所有layers减去4个layers,32-4=28个layers是设置的。

2.1.2.2 需求

需要显卡支持Multi Render Target,Shader Mode 3.0+,Depth render texture。2006年以后的PC显卡,从GeForce8XXX以及RadeonX2400,IntelG45开始,都支持。

在移动端,需要OpenGL ES 3.0。

注意:延迟着色不支持正交投影,如果摄像机是用正交模式渲染,则会对其启用前向渲染通路。

2.1.2.3 效率

延迟着色中光照负载,只跟照亮像素的光源数量有关,而跟场景复杂度无关。所以将点光源或聚光灯光源的范围降低可以降低负载。如果光源整体或部分被场景对象遮挡,则效率更高(照亮的范围更小)。

如果光源需要渲染阴影,则极大的增加负载。对于每个要投射阴影的对象,每个光源都要进行额外的渲染。同时,渲染阴影的光源,在shader代码效率上也远低于不渲染阴影的shader。

2.1.2.4 实现细节

如果对象不支持延迟着色(比如半透明),则它们将在延迟渲染过程完成后,在前向渲染通路中完成。

G-Buffer(Geometry buffer)对RT0-RT4(render target)的使用如下,数据被渲染到每个render target 的不同通道中:

  • RT0, ARGB32 : Diffuse color 漫反射颜色 (RGB), occlusion(A).
  • RT1, ARGB32 : Specular color 高光颜色 (RGB), roughness 粗糙度(A).
  • RT2, ARGB2101010 : World space normal 世界空间法线 (RGB), unused(A).
  • RT3, ARGB2101010(non-HDR) 或 ARGBHalf(HDR) : Emission + lighting + lightmaps + reflection probes
  • Depth + Stencil buffer 深度+模板缓冲区

因此,默认的人G-Buffer是每个像素160位(non-HDR)或192位(HDR)。

如果使用了 Shadowmask 或 Distance Shadowmask 进行混合渲染,则会用到RT4:

  • RT4, ARGB32 : Light occlusion values(RGBA) 光照遮挡值.

这时,G-Buffer则是每个像素192位(non-HDR)或224位(HDR)。

如果硬件不支持5个并行的render target,则使用了shadow masks 的对象,将会由前向渲染管线来渲染。当摄像机未开启HDR时,Emission+lighting buffer (RT3)使用对数编码方式,来提供比普通的ARGB32贴图格式更大的动态范围。

注意,当摄像机使用HDR渲染时,并不会为Emission+lighting buffer (RT3)单独创建render target,而是将RT3创建为ARGBHalf,每个通道是16位,这样用同一个RT存储。

(Note that when the Camera is using HDR rendering, there’s no separate rendertarget being created for Emission+lighting buffer (RT3); instead the rendertarget that the Camera renders into (that is, the one that is passed to the image effects) is used as RT3.)

G-Buffer Pass

G-Buffer位每个对象渲染一次,将包括漫反射和高光颜色,表面平滑度,世界空间法线,以及emission+ambient+reflections+lightmaps渲染到G-Buffer中。G-Buffer被创建成一个全局的属性,这样后面的shader可以访问(命名为CameraGBufferTexture0 … CameraGBufferTexture3)。

Lighting Pass

lighting pass 基于G-Buffer和深度在屏幕空间计算光照,因此负载跟场景复杂度无关,光照被累加仅emission buffer。

与摄像机的近裁剪面不相交的点光源和聚光灯光源(完全在摄像机近裁剪面的前面),利用场景的Z-buffer测试,进行3D渲染。这样可以将被遮挡的光源过滤掉,提升性能,即对光源进行深度测试,失败了就不进行光照运算了。而对于与近平面相交的方向光,点光/聚光等光源,则渲染成四方形面片。

如果开启了阴影,则也在该阶段处理。但是这需要对投射阴影的对象额外渲染,并在该阶段使用更加复杂的shader。

在该渲染管线中,仅可以使用Standard光照模型。如果需要不同的光照模型,则修改lighting pass用的shader,通过将 Built-in shaders 里的Internal-DeferredShading.shader的文件,放到Assets/Resources目录下,并进行修改。然后,在ProjectSettings>Graphics,将Deferred下拉列表,改为Custom Shader,并指定我们改好的shader。

2.1.3 老版延迟着色 Legacy Deferred rendering path

关于改着色技术理论,参考 this article 。

老版本的延迟着色,是为了兼容Unity5.0版本,主要是因为该版本不支持一些渲染特性(比如standard shader,reflection probes)。新项目不建议再使用该版本管线。

注意:延迟着色不支持正交投影,如果摄像机是正交视图的,则会使用前向通路渲染。

2.1.3.1 概述

使用延迟着色时,每个对象能够接收的点光源数量没有限制,所有的光源都将按照逐像素渲染,意味着都能跟光照贴图正确地配合,以及支持cookies和阴影。

延迟着色地优势是负载跟照亮一个像素的光源数量是成比例的。该负载只跟光源在场景中的体积(照亮范围)有关,而跟它照亮的对象数量无关。因此提升效率的方法之一,就是让光源的照亮范围小一些。延迟着色具有一致性,可以按照我们预想的进行渲染。每个光源都是逐像素的,因此再比较大的三角形上形成颜色块(基于顶点的光照因为只计算了顶点颜色,并再像素阶段进行插值而导致一些色阶)。

延迟着色的缺点是不支持抗锯齿和半透明物体(这些物体将在前向渲染中处理)。同时不支持对特定对象是否接受阴影的设置,并且culling mask也仅是有限度的支持:最多支持4个culling masks,也就是说culling layer mask 至少要包含所有layers减去4个layers,32-4=28个layers是设置的。

2.1.3.2 需求

需要显卡支持Shade Mode 3.0+,Depth render texture,双面stencil buffers。2004年以后的PC显卡,GeForce FX,Radeon X1300,以及Intel 965/GMA X3100 以后的显卡,都支持。

2.1.3.3 效率

延迟着色中光照负载,只跟照亮像素的光源数量有关,而跟场景复杂度无关。所以将点光源或聚光灯光源的范围降低可以降低负载。如果光源整体或部分被场景对象遮挡,则效率更高(照亮的范围更小)。

如果光源需要渲染阴影,则极大的增加负载。对于每个要投射阴影的对象,每个光源都要进行额外的渲染。同时,渲染阴影的光源,在shader代码效率上也远低于不渲染阴影的shader。

2.1.3.4 实现细节

Base Pass

base pass 对每个对象渲染一次,摄像机空间的法线和高光强度,被渲染到一张ARGB32的Render Texture
上(法线存储在RBG通道,高管强度存储在A通道)。如果平台或硬件支持将Z-buffer当贴图进行访问,则可以不用渲染深度,否则需要一个额外的渲染通路用shader replacement来渲染深度。

Base Pass主要是根据场景填充Z-buffer,以及存储像素的法线和高光强度的Render Texture。

Lighting Pass

lighting pass 基于深度,法线,高光强度来计算光照。因为计算是在屏幕空间的,所以效率跟场景复杂度没有关系。光照图是一张ARGB32(32位)的render texture,RGB通道用来存储漫反射颜色,A通道存储单色的高光强度。光照计算的值经过对数编码,以达到更大的动态范围。如果摄像机打开了HDR,则缓冲区改用ARGBHalf(64位),也不会执行对数编码了。

对于点光/聚光灯光源,与摄像机近平面不相交(完全在视锥体内)的,渲染成3D形状(考虑球形),同时进行深度测试,如果像素深度小于光源深度,则受该光源影响。对于与近平面相交的光源(可能光源的位置在摄像机近平面的后面),同样也会渲染3D形状,但是深度测试相反,大于光源深度的像素,受该光源影响。这样部分或全部被遮挡的光源,渲染效率就特别高了。如果一个光源同事与近平面和远平面相交,前面提到的优化就不会被执行了,不会执行深度测试,光照会被渲染成一个面片。

方向光不会执行上述优化,而是全屏计算。

如果光源开启了阴影,则也在该阶段处理。但是这需要对投射阴影的对象额外渲染,并在该阶段使用更加复杂的shader。

该通路下,只能使用Blinn-Phong光照模型,如果需要不同的光照模型,则修改lighting pass用的shader,通过将 Built-in shaders 里的Internal-PrePassLighting.shader的文件,放到Assets/Resources目录下,并进行修改。然后,在ProjectSettings>Graphics,将Lagacy Deferred下拉列表,改为Custom Shader,并指定我们改好的shader。

Final Pass

Final Pass 生成最终渲染图像,在这一步中,所有的对象需要再渲染一边,并利用光照图获取光照信息,合并贴图颜色,及其它自发光光照。LightMaps也是再这一步中进行应用。距离摄像机近的,渲染实时光照,并叠加烘焙的方向光光照。距离摄像机远的,过渡到完全的烘焙光照。

2.1.4 顶点光照渲染 Vertex Lit Rendering Path

该通路对没个对象渲染一次,并在顶点阶段,对每个顶点执行所有的光源的光照运算。

该方式速度最快,同时绝大多数的显卡都支持。

因为所有的光照都是在顶点中完成的,该通路不支持很多逐像素渲染的特性,比如:阴影,法线贴图,light cookies,高细节的镜面高光都不支持。

2.2 内建渲染管线的硬件需求Hardware Requirements for the Built-in Render Pipeline

2.2.1 概要

Win/Mac/Linux iOS/Android Consoles
Deferred lighting SM3.0, GPU support - Yes
Forward rendering Yes Yes Yes
Vertex Lit rendering Yes Yes -
Realtime Shadows GPU support GPU support Yes
Image Effects Yes Yes Yes
Programmable Shaders Yes Yes Yes
Fixed Function Shaders Yes Yes -

2.2.2实时阴影 Realtime Shadows

实时阴影在大多数的PC,手机,主机平台上都支持。在Windows系统上(Direct3D API),显卡都要支持阴影映射;多数2003年后的独立显卡,以及2007年以后的集成显卡都支持。技术上,支持Direct3D9的显卡,都要支持D16/D24X8 or DF16/DF24 贴图格式。在OpenGL上,还要支持GL_ARB_depth_texture。

移动端阴影(iOS/Android)需要OpenGL ES 2.0 以及 GL_OES_depth_texture格式扩展,或者支持OpenGL ES 3.0。因为贴图扩展格式在Tegra3,4上不支持,所以在这些硬件上,也不支持阴影。

2.2.3 后处理效果 Post-processing Effects

Post-processing effects 后处理效果,需要render-to-texture功能(向一种特殊的贴图上渲染),目前显卡都是支持的。

2.2.4 Shaders

可以编写可编程shader或固定功能shader(这里指的是Unity shader,固定管线没有shader)。在Shader Mode2.0(PC)以后和OpenGL ES 2.0(移动端)以后,都支持可编程shader。如果需要更多的功能,可以选择更高的shader models。固定功能渲染除了主机,所有平台都支持。

3. 通用渲染管线 Universal Render Pipeline

Universal Render Pipeline (URP),通用渲染管线是预建的一种可编程渲染管线,URP提供了一种易于美术使用的工作流,快速容易地创建优化地多平台地图形,从移动端,到高端主机,到PC。

更详细地URP,参考URP package documentation microsite。

4. 高级渲染管线 High Definition Render Pipeline

HDRP让你可以位高端主机平台创建技术领先地,高逼真度地图形。

用HDRP创建AAA级地游戏,汽车演示,建筑应用,以及其它需要高逼真度地图形。HDRP使用基于物理地渲染和材质(PBR),支持前向和延迟着色。HDRP使用了compute shader 技术因此需要硬件支持。

参考:HDRP package documentation microsite

5. 可编程渲染管线 Scriptable Render Pipeline

Scriptable Render Pipline允许在C#脚本中控制渲染,进行各种高级地定制。

Unity提供了2中于定义地SRC,分别是URP和HDRP,这两种都提供了大量定制选项。如果需要对渲染进行更多地控制,可以从头开始写一个全新的SRP。

可以从头开始创建全新的SRP,也可同通过修改URP和HDRP来满足需求。

更多信息参考SRP Core package documentation microsite.

5.1 SRP Batcher 合批

SRP Batcher,是针对场景里,使用同一个shader的变体的许多materials的对象进行处理,以提升CPU渲染效率的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lY5ZLGFO-1581473824690)(https://docs.unity3d.com/uploads/Main/SRPBatcher.png)]

5.1.1开启SRP合批

只有使用SRP的项目,才能开启SRP Batcher,包括:URP,HDRP,自定义SRP。

位URP,HDRP开启Batcher:

  1. 在Project窗口,找到UniversalRP-High/Low/MediumQuality.asset并选中。
  2. 在Inspector窗口,在Advanced部分,勾选开启,如下图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dPbMy3G5-1581473824690)(https://docs.unity3d.com/uploads/Main/SRPBatcher_Asset.png)]

也可以在代码中修改:

GraphicsSettings.useScriptableRenderPiplineBatching = true;

如果是XR项目:

XRSettings.stereoRenderingMode = XRSettings.StereoRenderingMode.SinglePassInstanced;

5.1.2 各个平台,支持合批的Unity版本:

Platform Minimum Unity version required
Windows DirectX 11 2018.2
PlayStation 4 2018.2
Vulkan 2018.3
OSX Metal 2018.3
iOS Metal 2018.3
Nintendo Switch 2018.3
Xbox One DirectX 11 2019.2
OpenGL 4.2 and higher 2019.1
OpenGL ES 3.1 and higher 2019.1
Xbox One DirectX 12 2019.1
Windows DirectX 12 2019.1

5.1.3 SRP Batcher 工作原理

在Unity中,可以在任何时候修改任何材质的属性,但这回导致效率损失。例如,DrawCall在使用一个新的材质时,需要做做很多初始化工作。所以场景中的材质越多,CPU需要花费更多的时间位GPU准备数据。传统的方法时通多降低Draw Call调用次数,来降低这方面的CPU消耗。因为真正导致效率问题的,是位调用DrawCall做的准备工作,而DrawCall本身相对较少,主要是向GPU指令缓存提交数据。

SRP Batcher通过将一系列的Bind和Draw的指令合并到一起,降低设置GPU产生的负载,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6hhQ3QZ-1581473824691)(https://docs.unity3d.com/uploads/Main/SROShaderPass.png)]

如上图,左边是标准的一次DrawCall,需要做的准备工作,远高于右边的一次DrawCall。

(译者:在自研引擎时,对于GPU的指令也是有缓存的,通过在内存中缓存,每次设置GPU状态时,判断是否改变,如果没有改变,就不设置了,比如Driect3D.SetRenderState(…)。这种缓存是分层的,底层是GPU API级别,如SetRenderState,SetTexture,SetShader,等。上层是材质级别,材质级别需要对渲染对象进行材质排序。细节实际上很复杂,这里只是提一下概念原理。)

为了达到最大的渲染效率,合批越大越好。位了能将更多的渲染合到一个批次,要尽量使用相同的shader建立不同的material,减少shader变体的数量。

在内部的渲染循环中,当Unity发现一个新的材质,CPU会收集所有的需要设置到GPU的属性,调用GPU API 在GPU现存中建立常量缓冲区。GPU Buffer的数量,取决于Shader如何声明自己的CBUFFERs。

位了提升使用了较少的shader变体创建了很多材质使用的场景,SRP在CPU对GPU数据状态进行了缓存。

SRP Batcher 是一个底层的渲染循环,作用是减少对GPU 显存的修改次数。如果材质没有发生改变,则不会设置和更行GPU现存内容。SRP建立了专门的代码流程,来快速的在一个大的GPU Buffer中更新引擎各种属性,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lDzUqOTb-1581473824692)(https://docs.unity3d.com/uploads/Main/SRP_Batcher_loop.png)]

这里,CPU只处理Unity引擎定义的属性,上图中的Per Object large buffer。所有的材质,都在GPU中有固定的CBUFFER,随时可以使用。这对渲染提速在于:所有的材质内容都保留在了显存中,有专门的逻辑来为每个对象属性管理per-object GPU CBUFFER。

(译者:从该篇幅可以看出,主要还是一种缓存机制,但是能将缓存在内存和显存中进行更加有效的映射,同时所有的材质都在显存中缓存了,很多GPU API 调用都可以省掉了。具体原理,还是只能理解个大概)

5.1.4 SRP Batcher 的兼容性

SRP渲染的Object需要:

  • 必须是Mesh或Skinned Mesh,不能是粒子。
  • Shader必须兼容SRP Batcher,HDRP和URP的Lit,Unlit都兼容(除了粒子版本)。

Shader 兼容性:

  • 需要在名字为“UnityPerDraw”的CBUFFER中声明引擎内建的属性。例如,unity_ObjectToWorld, or unity_SHAr
  • 需要在名字为“UnityPerMaterial”的CBUFFER中声明材质属性。

可以在Shader Inspector 面板中查看兼容性:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JaQJE6v4-1581473824692)(https://docs.unity3d.com/uploads/Main/SRP_batcher_shader_compatibility.png)]

Unity会自动识别兼容SRP Batcher的对象,在SRP Batcher 逻辑路径渲染,非兼容对象也会被Standard SRP 逻辑路径渲染。

Using the SRP Batcher code path Not using the SRP Batcher code path
* 使用兼容SRP Batcher 的Sahder的Mesh * 不是mesh的(包括skinned mesh)
* Shader不兼容的
* 使用了 Material Property Block 进行渲染的

5.1.5 SRP Batcher 性能剖析

为了验证SRP Batcher 对渲染性能的提升,可以向场景中添加 SRPBatcherProfiler.cs 脚本(创建一个空对象,添加脚本),来查看。当该脚本运行时,使用F8键显示/隐藏剖析窗口,F9键开启/停止Batcher。

界面显示如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJbXRf7K-1581473824692)(https://docs.unity3d.com/uploads/Main/SRP_Batcher_overlay.png)]

该系统统计了SRP渲染的CPU时间占用。

这里的时间累计了一帧的所有的RenderLoop.Draw and Shadows.Draw的时间,无论在哪个线程。例如SRP占用了1.31ms,可以是在主线程draw call占用了0.31ms,1ms是在graphic jobs执行占用的。

显示信息

名字 描述
(SRP batcher ON) / (SRP batcher OFF) 当前Batcher是否工作。F9键开启/关闭
CPU Rendering time SRP循环占用的总CPU时间,无论使用的哪种线程模型,例如single(单线程), client/worker(多线程) or graphics jobs(Job系统)。根据该参数可以看到Batcher的效果,通过F9开启/关闭Batcher来对比该CPU时间,来进行对比。上图例子中是2.11ms。(incl RT idle):表示渲染线程的空闲时间,这意味着是client/worker模式,而没有graphics jobs,渲染线程需要等待主线程的Graphics Commands。该例子中,渲染线程等待了0.36ms。
- SRP Batcher code path (flushes) b标识了SRP Batcher 逻辑占用的CPU时间。该时间被分成了渲染所有对象(1.18ms),和渲染对象阴影(0.13ms)的时间。如果阴影数量比较多,尝试降低场景中投射阴影的光源的数量,或者将SRP Asset配置中的Cascades参数降低。该例中,是1.31ms。
flush(s)数量标识了根据shader变体,场景被flush了几次(渲染了多少批次),这里是89次。该值越低越好,越低标识该帧中用到的shader变体越少。
- Standard code path (flushes) 标识了SRP非Batcher逻辑占用的CPU时间。例如骨骼动画模型和粒子。在该例子中,渲染了81个对象,消耗0.8ms,其中影子0.71ms,其它的0.09ms。
Global Main Loop: (FPS) 主循环时间,等于FPS时间。注意:FPS并不是线性的,如果看到FPS提升了20,并不意味着效率提升了。按F9开启/关闭SRP Batcher,对比CPU Rendering time

5.1.6 Unity Frame Debugger 中的SRP Batcher 数据

可以在Frame Debugger窗口中查看SRP Batcher的合批情况。

  1. Window > Analysis > Frame Debugger ,左侧列表中,Render Camera > Render Opaques,展开 RenderLoopNewBatcher.Draw 列表,查看合批次数。
  2. 点击任意的 SRP Batch 查看其状态。

SRP Batch 细节显示了调用了多少次DrawCall,Shader附加的关键字,为什么没有于上个批次合到一起。在下面的例子中,原因是:Node use different shader keywords,即该批次使用了与上个批次不同的shader keywords,因为使用了不同的shader变体,合批被打断了。如果SRP的DrawCall次数较低,说明很可能用到了太多的shader变体。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-foI634iK-1581473824693)(https://docs.unity3d.com/uploads/Main/SRP_Batcher_batch_information.png)]

如果要实现自己的SRP,而不使用URP或HDRP,尝试写一个使用最少的shader keywords的通用的’uber’ shader (但是可以根据需要使用材质参数和属性,没有太大限制)。

你可能感兴趣的:(Unity 渲染管线)