GPU动画

unity官方的方案:Animation Instancing:高性能大规模动画解决方案 - 技术专栏 - Unity官方开发者社区

Unity-Technologies/Animation-Instancing: This technique is designed to instance Characters(SkinnedMeshRender). (github.com)

知乎:GPU Skinning 加速骨骼动画 - 知乎 (zhihu.com)

这是一个网友的,但是他收费的:

2D割草/吸血鬼游戏 性能优化——GPU Spine动画_spine gpuinstance-CSDN博客 

【Unity】GPU骨骼 GPU Spine动画 2D/3D渲染性能开挂 合批渲染 支持武器挂载 动画事件 动画融合 实时获取骨骼位置_gpu骨骼动画-CSDN博客

 

使用目的

当场景里有数量庞大的角色,我们通常会用GPU Instancing去合批,以降低每个角色的渲染都要向GPU提交数据造成的巨大DrawCall.

而当这些角色都拥有骨骼动画的时候,GPU Instancing便会失效,因为它默认不支持Skinned Mesh Renderer,导致合批失效。

因为骨骼动画是由CPU计算的,每帧CPU都在通过动画文件计算角色的模型的新顶点数据,然后再把这些顶点数据传给GPU,这个过程造成了巨大的DrawCall

解决方法是,将角色的动画数据转为一张纹理贴图,然后传给GPU,GPU去解析动画并且实时改变模型顶点位置,来达到同样的播放动画效果

 

帧动画

这个比较简单,就是将2D序列帧合并到同一张图集,然后在Shader里播放,不同的时间,采样的纹理的uv不同,来实现播放动画的目的

优点:性能非常高,一般用于简单的2D帧动画,粒子动画

 

顶点动画

将角色的动画转为一个二维的数据结构,一维是时间,二维是动画的帧数据(每帧的顶点位置)

优点:简单容易使用,效率高,shader里直接读取顶点信息,不过进行过多的计算

缺点:如果模型的顶点数据过多,动画过多,动画过长都会导致生成的动画纹理过大,显存占用大,反而降低性能

适用于:模型比较简单,动画比较简单的情况

[Unity] GPU动画实现(一)——介绍-CSDN博客

[Unity] GPU动画实现(二)——网格合并_unity 网格合并-CSDN博客

[Unity] GPU动画实现(三)——材质合并_unity合并材质与贴图-CSDN博客

[Unity] GPU动画实现(四)——生成动画数据_unity computershader实现大批量动画-CSDN博客

[Unity] GPU动画实现(五)——渲染GPU动画_游戏引擎 游戏开发 [unity] gpu动画实现-CSDN博客

 

骨骼动画

下面这篇文章,油顶点动画实现的方式,也有“矩阵(变换)动画”的方式,又有人称骨骼动画,据他说是可以节省空间,还没细看搞懂原理

大致原理应该是存储骨骼的运动信息,然后在shader里去代替cpu进行骨骼的运算,骨骼的运动会引起一系列顶点的运动,所以可以不用存储每个顶点的信息。简而言之就是,“时间换取空间

适用于:模型比较负责,比如人物的动作

Unity-GPU Animation - 知乎 (zhihu.com)

 

缺点&解决方案:

1.无法获挂载点

2.无法实现动画融合

3.无法获取动画帧事件

顶点动画骨骼动画都是在GPU跑动画,因此不能做过多复杂的运算,像动画的融合问题。比如人物从奔跑到停下,不是瞬间从奔跑切到停止的,实际上会慢慢停下来。

美术只做了run跟idle两个动画,然后Unity的动画混合树会自动在这两个动画做插值,实现fade效果

如果用了gpu动画,我们就没办法实现一个动画fade到另一个动画的效果,只能是“闪”切过去的,会卡一下。

看B站的时候,在评论区里有人提到了相关的解决方法,关键字:

  1.回复 @FinalGames : 过度的我记得asset store有个插件了,他是encode动画骨骼位置和顶点相对于骨骼的位置,然后shader根据骨骼位置算顶点位置,这样就可以lerp骨骼位置,然后再计算顶点,最后才是顶点到pixel的计算。

  2.大概用”团结引擎 - 手册: 虚拟几何体 (unity.cn)“实现了GPU渲染动画带融合,参考:“所见即所渲”,西山居新作《解限机》这样使用Virtual Geometry - GameRes游资网 或 “所见即所渲”,西山居新作《解限机》这样使用Virtual Geometry (qq.com)

    里面提到:”以前是用传统的 GPU SKINNING,要把动作烘培到贴图上,但是这样的话,动作融合、IK、RIG动作融合,都没有办法使用。因为我们的游戏对动作要求比较高,所以必须要使用这些功能。为了解决这问题,我们决定在这些动作系统计算完骨骼动画后,再把数据上传到 GPU 显存中,在 GPU 里面做蒙皮动画计算。最后在这一个 Drawcall 里面通过 VG 渲染,单个 DrawCall 全部绘制上去。就算同一个机甲,玩家可以有不同的涂装,有不同的纹理,不同的颜色,这些都是可以支持的。“

 在这篇文章里作者有提到解决方案,但是他的源码不提供,要钱的:【Unity】GPU骨骼 GPU Spine动画 2D/3D渲染性能开挂 合批渲染 支持武器挂载 动画事件 动画融合 实时获取骨骼位置_gpu骨骼动画-CSDN博客

三,获取挂点位置
例如GPU动画人物手里拿着一把枪,发射子弹时就需要在枪口的位置创建并发射子弹,由于GPU动画已经没有了骨骼Transform,枪口的位置怎么获取呢?

GPU动画因为是纯Shader实现,所以切换动画只需要修改材质的ClipId属性即可,其中x作为动画索引,y作为动画播放的开始时间,即Time.time。

有了动画索引和播放的开始时间,我们就可以得到当前动画已经播放了多久,根据已经播放时长就可以算出动画播到了第几帧,通过第几帧就可以从动画贴图读出任意骨骼的矩阵,这样就实现了随用随取的高性能获取挂载点位置、旋转、缩放。

四,GPU动画帧事件:
GPU动画转换工具会自动把Animation Clip中包含的事件数据保存到文件里,无需手动处理。并且支持随意增删事件。

GPU动画事件同时支持 Mesh Renderer渲染和BRG渲染。

两种渲染模式触发逻辑不同:

1. 使用MeshRenderer渲染只需挂载一个事件脚本,然后就像为Button添加/移除监听事件一样简单。

2. 使用BRG渲染, BRG提供了获取触发事件的接口,接口使用Jobs检测当前帧触发的事件,并将事件列表返回,由用户在主线程自行调用触发。大大提升了海量GPU动画单位事件触发性能。

同时GPU动画事件处理时会进行插帧计算,不会因为卡顿问题导致跳过动画事件的触发。

例如:一个弓箭手射箭动画,动画前大部分是搭箭、拉弓,动画最后一帧才松手,为了显示效果同步,就需要在最后一帧弓箭手松手时让箭发射出去,而不是在搭箭/拉弓的时候就发射箭。通过使用帧事件就能完美卡点解决这个问题。

五,GPU动画过渡/融合:
目前市面上的GPU动画插件要么是没有动画融合,要么就是使用脚本计算融合,会导致性能大打折扣。为了保证性能优先、兼容性高,最佳方案还是纯shader处理。

如何用最小的代价实现GPU动画平滑过渡呢?GPU动画切换动画时需要修改material上的shader参数clipId(Vector4),其中x为动画clip的索引,y为动画切换时间,zw是预留属性,暂未用到。

已知clip索引和切换动画的时间就可以轻易计算出动画已经播了多久,进而计算出播到第几帧,然后通过帧数就可以从动画贴图中读取到当前帧所有骨骼的Transform信息,然后所有骨骼从当前的Transform数值平滑过渡到下一动画clip索引的第一帧骨骼Transform不就能实现骨骼平滑过渡了吗?我们还需要指导上一个动画索引和上一个动画开始播放的时间,正好存入预留的zw中。

 

posted @ 2024-06-10 23:21  JeasonBoy  阅读(44)  评论(0编辑  收藏  举报