Unity 实现传统的GPU Skinning

1.Unity5.6目前对Skinning的处理有两种模式:

a)一种是CPU Skinning,多线程+SIMD,性能实测非常不错

b)还有一种是unity GPU Skinning,但它与传统的vertex shader做顶点混合不一样,是通过transform feedback+ vertex shader (opengl)/ stream output + geometry shader(dx11)做顶点混合,写到新的GPU端verticesbuffer后,再提交一次一般模型的渲染完成的.如果这个Skinning模型会触发多遍渲染(shadow pass/reflecionpass/..),理论上相对传统GPU Skinning能节省一些顶点混合的计算而变快,实测PC上确实有5%左右的提升(仍然CPU Bound).

2.因为实测中发现Unity这两种做法目前都是CPU Bound,理论上传统的GPU Skinning会更快(1 pass),所以我尝试在Unity5.6上实现了传统的GPU Skinning,并在C#上尽力做了一些优化和处理...

3.主要流程:

a)修改原Mesh,让顶点数据带上BoneWeight所有信息

b)替换SkinneMeshRenderer为普通MeshRender,切换新的传统GPUSkinning材质及shader

c)每帧动画做完后,计算Bone Palette,并传给shader渲染。

d)Vertex Shader里做顶点混合相关计算,这里v.texcoord1对应上面的indices,v.texcoord2对应上面的weights

4.尝试优化

a)通过上面的流程已经能完成传统的GPU Skining了,但是因为上面的‘c)计算Bone Palette’过程是在主线程完成的(C#),加上Unity自身对Transform非主线程访问的限制,相对Unity5.6的多线程CPU Skining,性能不理想,当然,如果是单线程应该是变快了...

b)第一个优化就是打包顶点数据,看上面代码,打开C#的宏PACK_WEIGHT_AND_BONEID,打开vertex shader里对应USE_PACKED_ID_WEIGHT,因为BoneIndex是整数索引,weight 0.0-1.0f,weight缩小一点点再加在一起传入,shader读出还原,指令简单。这个做法需要高精度浮点值支持,手机目前是不行的,(参考链接https://community.arm.com/graphics/b/blog/posts/benchmarking-floating-point-precision-in-mobile-gpus),理论上也可以仔细做数值分析与打包,但shader会复杂化,很可能得不偿失。

c)第二个优化,很有创意,通过多构建一个特殊的SkinnedMesh(顶点数 == Bone Num x 4), 通过unity的BakeMesh接口,计算出了当前模型的Bone Palette(结果存在这个特殊的SkinnedMesh做BakeMesh后的vertices上),性能上比默认实现提升了40%+,主要区别就是C#计算BonePalette换成了BakeMesh的native simd代码计算,但是仍然在主线程。Unity源码层面,BakeMesh和Skinning.Skin是一套代码,主线程与Worker线程计算的区别。

i.构建特殊用途的SkinnedMesh,通过特殊的Vector3,使BakeMesh的计算结果可以简单反推Bone Palette。见下面代码的几行注释,分别取到了矩阵的4列,实际因为Unity的Position只能是Vector3,结果不是直接取到4列,需要再做简单计算。

ii.动画做完后CPU BakeMesh,复制出BonePalette,简单计算还原。

iii.上面的转置其实可以通过shader调整左乘右乘调整,但会增加一个Vector4,加上开销较小,就不做这个处理了。

iv.实际的测试模型,有100+根骨骼,这Shader目前只支持最大64根,C#端做了拆分处理(代码很长,不贴了)。

v.性能测试

PC上,30个人渲2遍,增加了9.3ms的GPUSkinRender.LateUpdate开销,BonePalette通过BakeMesh的计算就在这里(理应快很多,因为BakeMesh做了些额外的事情)。如果是直接bones[j].localToWorldMatrix * bindPoses[j];会是15.0ms左右。这一段计算合理的话应该在Worker线程完成的,但在C#层面无法修改或者得不偿失(可以自己用额外的分线程去计算,但是Transform需要提前Copy,按以往经验,快不了多少).

PC上的同样的角色做CPU Skinning,Worker线程计算MeshSkinning.Skin非常快,主线程的overhead几乎可以忽略,性能上完胜.

5.结论

Unity CPU Skinning因为多线程与SIMD优化,性能非常好,在目前多核CPU时代非常有优势。在PC上,UnityGPU Skinning亦有更好的性能,实测下来是目前最优方案,也确实看到有游戏使用了(PC端的女仆2)。在Unity上实现的传统GPU Skinning,通过C#层面的优化,综合性能并不理想,主要原因是Unity的限制导致Bone Palette无法高效的多线程计算。但理论上,在GPU有余力时,Bone Palette如果能高效多线程计算,性能应该会上升.后续我会尝试在Unity源码层面做一些修改,希望能取得更优的移动平台性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值