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,atv,切换新的传统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,简单计算还原。 (责任编辑:本港台直播) |