美文网首页
基于Shader的动态合批

基于Shader的动态合批

作者: 李利知灬 | 来源:发表于2022-03-11 20:34 被阅读0次


    前言

        关于unity动态合批,一直被人诟病的是他的使用条件太过苛刻,上限900左右个顶点数,只能用来合批一些小物件。

        毕竟在Update上面每帧计算900个顶点数据,对Cpu是一个不小的消耗,所以我们不妨设想一些,如果我们把顶点的运算放到GPU上面,我们是不是就可以无视顶点上限的问题了。

        其实我最近一直在做关于性能优化的事情,关于合批上面的事情也做了很多,发现其实很多情况下,静态合批的情况下,也需要做一些运动等,但是unity自带的动态合批可用性实在是小的可怜,在各方面的问题下,我们依然还是决定自己做一个条件更少,适用性更广泛的合批。来优化我们项目的一些功能。

    效果

        话不多说,先看一下测试场景对比效果。


    Shader合批前后的 Batches和对比
    支持运动,运动前后的帧率对比

    对比

      相比于unity,他是把定点的运算都放到了cpu的update里面,每帧去更新顶点的位置,因为对cpu的消耗十分巨大,所以顶点的最大数量始终在900左右。但是cpu对于大量的计算能力,始终不如GPU,所以我们要做的就是把顶点的计算都放到GPU里面,这样对于CPU的消耗几乎为零。所以基于Gpu的,顶点的数量可以说是 没有限制的,但是也不能大于uint32最大值(也就是10亿左右)。

      下面就是一些基于Shader的动态合批的一些基本原理。

    mesh合批实现原理

      对于合批的具体实现其实并不难,无疑是吧mesh的顶点,三角形数据,顶点光线等数据统一合并到一个大mesh中,这些可以自行百度了解。

      但是实际项目中我们可能同时合并很多个mesh,这是大量的mesh顶点数据需要在主线程中,会出现严重的掉帧现象,所以我们需要把顶点,三角形数据,顶点光线的计算放到子线程中,这里肯定会有很多小伙伴会问,不是unity的资源不能在子线程中访问嘛。

      所以我们需要分两步进行操作,首先在主线程把顶点,三角形数据,顶点光线等数据都收集数组中,第二步,然后把这些数组统一在子线程里面计算,如下图

    此图是在子线程中进行数据运算的过程。

        到这里,算是完成了合批的这一部分,这时候会有小伙伴问道,这个shader好像并没有什么关系,只是把mesh的运算放到了子线程了而已, 哼哼,下面关于顶点的运动就必须放到shader里面去了。

    基于shader 的顶点运算

          unity动态合批之所以顶点数量要求在900之内的原因,是因为把顶点的运算全都放到Cpu里面了。如果我们把顶点的运算放到Gpu 里面,那个对顶点数量的的要求将会彻底解除。

          呢么我们改如何去吧顶点的计算放入GPU呢,下面是两个猜想。

        设想1.  我们把通过ComputeShader,每帧将mesh的顶点,法线,传入GPU中,在GPU中计算过之后,在通过ComputeShader传过来。之后把数据重新赋给mesh。这样就可以实现他的运动了。缺点是每帧需要占用大量的宽带。并且需要三次数据的传递。

          设想2. 我们不改变mesh,只改变图形在屏幕中的显示。我们在合批mesh的时候,向顶点传入对应物体的序号,合批完成之后,我们只需要每一帧把改变物体的坐标矩阵传给shader中,呢么就可以实现物体的显示变换。每帧只需要传送每个子物体的矩阵即可,并且不需要CPU和GPU之间来回传递。

    毫无疑问,无论是哪一方面,第二种都要比第一种要好一些。

            既然是基于shader的,所以我们必须要专门为合批写一个 shader,CombShader里面的一些代码如下

    这里需要借鉴 骨骼动画的原理(原理类似),记录顶点的对应的bound。在GPU中找到bound对应的矩阵进行矩阵变换,就可以得到mesh在世界空间的坐标。


    将子物体对应的transfrom传给GPU

          把子物体的mesh数据都合并到父对象上面,只留下子物体对应的transform组件来记录更新矩阵数据。这样一来就是对animation动画的完美契合。并不需要把animation数据重新生成一份矩阵数据到本地。只需要每帧把需要改变的子物体矩阵传到GPU即可。

        通过上面两步就可以实现Shader的模拟运动。

    最终效果


    最终效果

      可以看到,原本800多个物体,全都合在两个mesh中了,Batchs从870+减到了17左右,顶点数在40万左右的数量,因为是shader的图形在动,所以动画动起来也对帧率几乎是没有影响。

    使用条件

    1.作为合批,避免不了的是,合批的对象使用的材质必须是一样的。(贴图可以不一样)

    2.顶点数不能超过uint32最大值。

    3.贴图的读写权限要开启。

    结束

        这个功能还是输入刚开始阶段,还存在一些问题,就比如在Update每帧要遍历子节点获取物体位置矩阵,增加了GC的频率。所以运动的时候偶尔会出现掉帧的现象。有兴趣的小伙伴可以研究一下怎么样更加高效的数据传递。

          功能有不足之处或者有问题可以加我哦:2364823151

    相关文章

      网友评论

          本文标题:基于Shader的动态合批

          本文链接:https://www.haomeiwen.com/subject/yyiurrtx.html