欢迎光临本站!

西山居工程师:《剑网3:指尖江湖》角色逼真摆动效果如何实现?

来源:原创    更新时间:2020-11-25 09:36:20    编辑:管理员    浏览:90

        近日,在中国Unity线上技术大会游戏专场中,西山居资深引擎开发工程师以“《剑网3:指尖江湖》客户端性能优化案例分享-动态骨骼(DynamicBone)优化”为主题发表演讲,介绍了DynamicBone优化、通过Burst Compiler和Mathematics数学库进行加速、以及动静分离的数据等,帮助各位开发者制作出更逼真的角色摆动效果。

以下是演讲内容,有删减:

大家好,我叫苏泰梁,来自西山居,现在主要负责《剑网3:指尖江湖》优化方面的工作。今天主要分享游戏动态骨骼DynamicBone的优化。

什么是动态骨骼?当使用动态骨骼时,拖拽角色左右晃动,头发、衣服的摆动都是比较真实自然的,动态骨骼和动作的融合也非常好。如果禁用所有动态骨骼的效果,在整个转动的过程中,头发、衣服都是硬梆梆的,非常僵硬。

动态骨骼是一款名叫DynamicBone的插件,一般用来模拟飘带、衣袖、裙摆、头发等的摆动效果,效果比较逼真,可以大大节省动作K帧的工作量。《剑网3:指尖江湖》的NPC、坐骑、各种挂件等都有用上,是一个使用非常广的功能。

右图的角色,我把它身上用到动态骨骼的地方都用数字标识了出来,1、2、3、4,有的长,有的短,都有动态的效果。

假设我们要给角色最长的飘带加上动态骨骼的效果,先找到这根飘带在对象树上的根结点,把它拖到动态骨骼的组件节点上,再配置一下参数就可以了,非常简单。

有很多参数,比如说阻尼系数、弹性系数、干性系数、惯性系数等等。阻尼系数可以理解为一种阻力或者摩擦力,节省速度。弹性系数会把你拉扯到一个目标位置。

接下来看 动态骨骼模拟简化过程。

还以飘带为例,假设它有5个节点,在上一帧处于垂直的状态,在当前帧稍微往右移了一点,有一点点的偏移、旋转,一般都是由于模型的位移或者动作带来的。除了根结点,其他的节点都会从上一帧的位置模拟,然后根据每个节点的参数,比如惯性系数、弹性系数等,对每个节点进行相关的模拟运算,得出一个最佳的位置。

最后,会把这个位置更新到每个骨骼节点上,同时根据父子节点的关系、位置,来修正这个旋转,这样的话整体上看起来就非常自然了。

每根骨骼在这个过程中要做什么事?核心的办法主要集中在组件的Update和LateUpdate。在Update中,每根骨骼需要重置位置,在LateUpdate中要做大量的模拟运算,并且最终会设置到骨骼。这里列出了完整的一个模拟运算。除了阻尼、弹性之类的,还有风力、重力各种模拟运算,印刷量比较大的。

然后,我们假设场景里面有30个角色模型,每个模型有10条骨骼链,每条骨骼链有10根骨骼,那一共有3000根骨骼,在《剑网3:指尖江湖》里20个玩家再加上坐骑、NBC之类的,3000根骨骼,数量是比较正常的。

这么多根骨骼,每根骨骼还要做这么多事情,性能怎么样?先看看优化前的数据。这是小米Max2在组成动态骨骼中的CPU消耗,占整个CPU消耗大概10%,这是一个非常大的开销。

动态骨骼为什么会这么耗?首先它的数量非常多,一共有3000根骨骼。二是它的运算很复杂,需要做大量的模拟运算。这里值得一提的是,在整个模拟运算的过程中,需要对每根骨骼进行世界坐标、世界旋转、世界矩阵等世界变化的获取和设置等操作。

说到世界变化相关的操作,在Unity里面要特别注意,因为这是一个非常耗时的操作。比如说获取和设置世界变化,在Unity的顶层并没有世界坐标的属性,只有局部坐标的属性,所以每次获取或者设置都是通过局部坐标一层一层地往上变,非常耗时。

在《剑网3:之间江湖》里面,很多骨骼的层数都是非常深的,比如下图,这里面挂了动态骨骼的效果,所以它的层级很深,大概有20层,它在计算世界坐标、旋转的过程中开销是非常大的。层级越深,消耗就会越大。

最后一点就是它的模拟是在Update和LateUpdate中完成的,每帧都需要做,也就是说这是一个固定的常态性能开销。

第一版优化

了解了它为什么这么耗,现在介绍一下我们做的 动态骨骼第一版优化。

下面这三点是我们做优化的过程中经常提起的一个三原则,第一个是能否不做,如果说不做也能达到效果,那肯定开销都没有了。二是必须做的话能否少做一些。最后不得不做的时候,再考虑能否做得更好。

再看看这张图,在1、2、3、4上面用红色标出骨骼的长度。下面还有张小图,已经很小了,4这根飘带还是非常长,看得还比较清晰,但是1和2已经看不大清楚了。所以就有了我们第一版优化思路,根据骨骼链屏幕投影程度,过短的骨骼链直接关闭动态骨骼效果。

投影长度可以使用骨骼链静态的长度。再加上游戏的FOV,一般动态变化比较少,所以计算它的屏幕投影程度可以做到几乎没有消耗。一般手机的宽度都是70毫米,2毫米以下就看不大清楚了。这个长度可以根据机型、机器情况和性能情况分不同的画质进行定制,还可以跟进不同的压力情况,实时调整。

能否少做呢?我们还是利用投影长度,把适中的骨骼,比如说1或者3的骨骼在某些情况下只保持最基础的刚性运算,保留最基本的效果。

再考虑能否做得更好。到了这步就只能死磕算法,在算法层面进行优化,尽可能地减少消耗。我们使用局部坐标,减少世界坐标,使用一些Catche来减少重复运算。最后减少Component数量,一个角色、一个组件就可以支持多条骨骼链的配置。这就是我们第一版的优化。

优化后的数据效果还是比较明显的,开销从10%直接降到6.5%,优化了大概35%的CPU开销。

不过,6.5%的CPU开销还是挺多的。既然有第一版优化,那就有第二版,接下来介绍一下我们第二版优化。

第二版优化

第二版的优化最好是不做,或者是少做。这里需要提到一个概念,就是Unity Job System,这是Unity提供的一套多线程编程框架。我们第二版优化的核心思想就是使用多线程,尽可能地减少主线程做的事情。

Job System是Unity提供的一套多线程编承框架,它跟一般的多线程有什么不同呢?为什么Unity需要额外提供一套多线程框架?在Unity中写过多线程的人可能都遇到过一个坑,一般的线程是没办法操作Unity对象的,这是Unity的一个强制限制,并且告诉你这个只能在主线程访问。直到Job System出现,才使得这成为可能。

虽然现在局限还是挺大的,但是它使得多线程中操作Unity对象成为可能。这就是第一个不同。

第二是它有强大的线程安全检测机制。这个是非常重要的,比如说主线程和Job线程之间一些数据的读写安全问题,都有强大的检测机制,保证你的数据不会写坏。

第三点,它有非常高的性能,可以充分地利用多核的CPU。Job System跟Unity引擎顶层的C++共享work线程池,work线程池会通过一个Job队列来减少上下文的切换和竞争问题,这样就可以充分地利用多核CPU的资源,从而提高性能。

接下来感受一下 Job System的简化例子。在这里,并行计算两个数据原始物中的一个值相加,然后复制到另外一个数据。首先我们要声明一个Job,然后集成Unity Job相关的接口,只用特定的数据结构证明自己的数据,就可以在Execute函数中写需要在Job中运行的逻辑了。这个例子也非常简单,Execute里面就是把A和B的数据元素直接相加,然后复制到C的数据中。

 

 

通过5G云游戏 编的介绍不知你有什么感想呢可以和小编我聊聊!!!!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,

不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 3424333933@qq.com 举报,一经查实,本站将立刻删除。

 

 

评论区

表情

共0条评论
  • 这篇文章还没有收到评论,赶紧来抢沙发吧~

相关内容

点击排行

随机新闻

评论排行榜