Unity自带的遮挡剔除
Unity默认是没有开启遮挡剔除的,只有视锥剔除,原因如下:
- 它可能会消耗大量的计算资源
- 它需要根据场景进行调整
- 它并不适用于所有场景
要启用遮挡剔除,需要在制作场景时将遮挡其他物体的物体设置为遮挡物(OccluderStatic):
我们可以通过Window/Rendering/Occlusion Culling找到这个面板。我这里把一个墙设置为遮挡物,几个Cube设置为被遮挡物,设置好后点击Bake:
运行场景,观察FrameDebug,可以发现后面的Cube都不再渲染了,作为对比可以看下未开启Occlusion Culling时的状态,后面Cube虽然看不见,但也会有对应的渲染流程。
所以,Occlusion Culling带来的影响有:
- 减少DrawCall
- 额外的计算剔除的耗时
- 更少的Overdraw与填充率
调整Bake选项卡下的参数有助于提高剔除计算的效率,Smallest Occluder(最小遮挡物)会影响充当遮挡物的最小物件的大小,值越小效果越好,但同时计算量也会上升,一般在2~5之间比较合适。Smallest Hole是可以看到的最小孔隙的大小,比如栅栏之间的间隙,值越小越精细,但同样会增加计算量,并且占用更多内存,0.2~0.5是比较合适的数值。最后一个参数,Backface Threshold可以减少剔除数据的大小,但是设置不当容易出现剔除错误,所以一般保持100%不动。
除此之外,还可以通过遮挡区域(Occlusion Areas)来设置需要计算剔除的区域,减少多余的剔除计算。
实际应用时,需结合具体的场景、想要达成的效果,以及带来的收益进行调整。此外,这种遮挡剔除的方式只适用于静态不透明物件,如果是场景特效,或者移动的物体就不再生效,另外需要手动设置遮挡物与被遮挡物,所以往往不能满足需求,而且在移动端CPU开销较大。
仿照Unreal的遮挡剔除
Unreal遮挡剔除的方法运行时消耗的CPU时间更少,所以我们可以仿照Unreal的方法自己实现一套。
首先需要将场景划分为许多格子,然后收集场景中所有的物体,默认获取Mesh上的包围盒,如果形状比较特殊可以由美术人员手动挂一个脚本来设定包围盒。
第二步计算每一个格子到每一个物体的可见性测试,先确定两个包围盒的面能否互相看见,当两个面上点的连线与两个面的法线的夹角都小于90°时,两个面就可以互相看见。对四个顶点进行判断,只要有一个点满足条件即可。
然后对两个面上的点做RayCast看是否可以连通,如果任意两个点连通,即两个AABB互相可见,两个面上的点可以采用随机采样,也可以按一定的间隔平均采样,也可以进行分层随机采样。
接下来就是对所有格子进行步骤二中的计算,直到结束,把信息保存在序列化文件中。
以上就是遮挡剔除的预计算的大致流程,这种方法写起来比较简单,但是预计算的耗时会比较长,所以还可以使用ComputerShader或者DXR实现加速。