UnityShader入门精要——Unity中的渲染优化技术(二)

减少DrawCall数目

最常见的优化技术——批处理。实现原理为减少渲染每一帧所需的drawcall数目。使用同一个材质的物体可以一起处理。

优点 缺点
动态批处理 切处理都是Unity 自动完成的,不需要我们自己做任何操作,而且物体是可以移动的 限制很多,可能一-不小心就会破坏了这种机制,导致Unity无法动态批处理一些使用了相同材质的物体
静态批处理 自由度很高,限制很少 可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动

1 动态批处理

如果场景中有一些模型共享了同一个材质并满足一些条件,Unity就可以自动把它们进行批处理,从而只需要花费一个draw call就可以渲染所有的模型。动态批处理的基本原理是,每一帧把可以进行批处理的模型网格进行合并,再把合并后模型数据传递给GPU,然后使用同一个材质对其渲染。除了实现方便,动态批处理的另一个好处是,经过批处理的物体仍然可以移动,这是由于在处理每帧时Unity都会重新合并一次网格。

条件限制:

  • 能够进行动态批处理的网格的顶点属性规模要小于900。
  • 一般来说,所有对象都需要使用同一个缩放尺度,但在Unity5中,这种对模型缩放的限制已经不存在了。
  • 使用光照纹理(lightmap)的物体需要小心处理。因此,为了让这些物体可以被动态批处理,我们需要保证它们指向光照纹理中的同一个位置。
  • 多Pass的shader会中断批处理。

要渲染这样一个包含了4个物体的场景共需要两个批处理。其中,一个批处理用于绘制经过动态批处理合并后的3个立方体网格,另一个批处理用于绘制球体。我们可以从Save by batching看出批处理帮我们节省了两个draw call。

现在,我们再向场景中添加一个点光源,并调整它的位置使它可以照亮场景中的4个物体。由于场景中的物体都使用了多个Pass的shader,因此,点光源会对它们产生光照影响。

渲染一帧所需的批处理数目增大到了8,而Save by batching的数目也变成了0。这是因为,使用了多个Pass的shader在需要应用多个光照的情况下,破坏了动态批处理的机制,导致Unity不能对这些物体进行动态批处理。而由于平行光和点光源需要对4个物体分别产生影响,因此,需要2x4个批处理操作。需要注意的是,只有物体在点光源的影响范围内,Unity 才会调用额外的Pass来处理它。因此,如果场景中点光源距离物体很远,那么它们仍然会被动态批处理的。

 

2 静态批处理 

Unity提供了另一种批处理方式,即静态批处理。相比于动态批处理来说,静态批处理适用于任何大小的几何模型。它的实现原理是,只在运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格结构中,这意味着这些模型不可以在运行时刻被移动。但由于它只需要进行一次合并操作,因此,比动态批处理更加高效。

尽管3个Teapot模型使用了相同的材质,但它们仍然没有被动态批处理。这是因为,Teapot 模型包含的顶点数目是393,而它们使用的shader 中需要使用4个顶点属性(顶点位置、法线方向、切线方向和纹理坐标),超过了动态批处理中限定的900限制。此时,要想减少drawcall就需要使用静态批处理。

静态批处理的实现非常简单,只需要把物体面板上的Static复选框勾选上即可。

现在的批处理数目变成了2,而Save by batching数目也显示为2。此时,如果我们在运行时查看每个模型使用的网格,会发现它们都变成了一一个名为Combined Mesh (roo:scene)的东西。这个网格是Unity 合并了所有被标识为“Static" 的物体的结果,在我们的例子里,就是3个Teapot和一个立方体。这个合并后的网格其实包含了4个子网格,即场景中的4个对象。对于合并后的网格,Unity 会判断其中使用同一个材质的子网格,然后对它们进行批处理。

 如果场景中包含了除了平行光以外的其他光源,并且在shader中定义了额外的Pass来处理它们,这些额外的Pass部分是不会被批处理的。但是,处理平行光的Base Pass部分仍然会被静态批处理,因此,我们仍然可以节省两个draw call。

3 共享材质

无论是动态批处理还是静态批处理,都要求模型之间需要共享同一个材质。但不同的模型之间总会需要有不同的渲染属性,如果两个材质之间只有使用的纹理不同,我们可以把这些纹理合并到一张更大的纹理中,这张更大的纹理被称为是一张图集(atlas)。 一旦使用了同一张纹理,我们就可以使用同一个材质,再使用不同的采样坐标对纹理采样即可。

如果调整微小的参数,如颜色,浮点属性等,一种常用的方法就是使用网格的顶点数据(最常见的就是顶点颜色数据)来存储这些参数。

需要注意的是,如果我们需要在脚本中访问共享材质,应该使用Renderer.sharedMaterial来保证修改的是和其他物体共享的材质,但这意味着修改会应用到所有使用该材质的物体上。另一个类似的API是Renderer.material,如果使用Renderer.material 来修改材质,Unity会创建一个该材质的复制品,从而破坏批处理在该物体上的应用,这可能并不是我们希望看到的。

4 注意事项

  1. 尽可能选择静态批处理,但得时刻小心对内存的消耗,并且记住经过静态批处理的物体不可以再被移动。
  2. 如果无法进行静态批处理,而要使用动态批处理的话,那么请小心上面提到的各种条件限制。例如,尽可能让这样的物体少并且尽可能让这些物体包含少量的顶点属性和顶点数目。
  3. 对于游戏中的小道具,例如可以捡拾的金币等,可以使用动态批处理。
  4. 对于包含动画的这类物体,我们无法全部使用静态批处理,但其中如果有不动的部分,可
    以把这部分标识成“Static"。

由于批处理需要把多个模型变换到世界空间下再合并它们,因此,如果shader中存在一些基于模型空间下的坐标的运算,那么往往会得到错误的结果。

一个解决方法是,在shader中使用DisableBatching标签来强制使用该Shader的材质不会被批处理。

另一个注意事项是,使用半透明材质的物体通常需要使用严格的从后往前的绘制顺序来保证透明混合的正确性。对于这些物体,Unity 会首先保证它们的绘制顺序,再尝试对它们进行批处理。这意味着,当绘制顺序无法满足时,批处理无法在这些物体上被成功应用。

猜你喜欢

转载自blog.csdn.net/weixin_51327051/article/details/125362689