Perception Neuron Unreal Plugin
本文的封面图标由我们的优秀设计师”Paoer“设计。
GDC 2017马上结束了。很遗憾由于近来公司事情比较多,没能够亲自飞到美国去。这篇文章给大家介绍一下去年GDC期间我为Notiom的一款动作捕捉设备开发的Unreal引擎插件,如果想了解更多Perception Neuron Unreal Plugin的可以看下面的介绍。
背景
15年末的时候通过子雄我认识了Noitom的CTO戴若犁博士。当时我们团队正在和Noitom进行一些研发方面的合作,于是我也借机接触到了Noitom研发的惯性动作捕捉设备Perception Neuron。那是我第一次接触动捕设备,感觉很神奇。当时Notiom已经为Perception Neuron开发了Unity插件,我们一起使用Unity引擎生产了一些技术演示的Demo。
在那个时候并没有好用的Unreal引擎的Perception Neuron插件。网络上有一些开发者开发了自己的版本,其中有一名叫Heiko Fink的开发者还获得了Epic的Unreal开发者资助计划提供的1.2万美元的奖金。
但是几乎所有的插件都有技术实现方面问题,达不到接入Noitom动捕产品的技术要求,而且还都停留在Demo阶段,没办法产品化。于是我开始着手为Noitom开发Unreal版Perception Neuron插件。
16年3月我在Unreal论坛Development Discussion下的Animation板块发布了题为“PerceptionNeuron Unreal4 plugin”的帖子,正式对外发布了我开发的Perception Neuron Unreal插件。
几天之后戴若犁博士带领团队去美国参加GDC 2016,并展示了由Noitom的工程师和Epic Games China的技术大牛们基于我的插件开发的一款技术Demo。
上面的视频是在GDC 2016期间由Myra拍摄的,谢谢Myra!
技术分析
下面给大家介绍一下Perception Neuron Unreal Plugin的一些技术实现细节。
网络传输
Perception Neuron的动捕数据通过网络传输到Axis Neuron中,Axis Neuron负责将动捕数据转发出去。Unreal Plugin与Axis Neuron之间的数据传输目前采用UDP传输。之所以采用UDP,是因为对于我们正在设计的产品Demo来说,采用广播的方式更加方便。由于Axis Neuron支持TCP传输,所以基于TCP协议的传输也可实现。
Axis Neuron每帧传送59根骨骼数据,每秒60帧,动补数据采用欧拉角表示。开启Axis Neuron的Displacement模式,会传输欧拉角和骨骼位移。非Displacement模式只传输欧拉角。所以每秒占用总网络带宽最高为:59 * 60 * (6 * 4) = 84,960 Byte,约83KB。
数据存储
在Unreal Plugin内部创建网络线程负责网络数据的接收工作。Axis Neuron发送过来的网络数据带有ID标识,所以在Unreal Plugin内部为每一个接收的新ID的网络数据创建一个数据缓冲区:
后续接收到的新网络数据,直接更新到对应ID的数据缓冲区即可。
多线程数据访问
为了防止多线程同时访问数据缓冲区造成数据竞争,要对数据缓冲区的访问进行加锁。但是同一时间只能有一个线程访问数据缓冲区,造成另一(或多个)线程等待,效率比较低。场景中可能有多个角色同时使用相同ID的动画数据,所以通过简单的加锁会导致多个角色之间没有必要的互斥访问。
针对这两方面问题我做了两处改进:
1.数据缓冲区使用双缓冲设计(Double buffer);
2.对数缓冲区加的锁为多读单写锁;
如上图所示,网络线程对Back buffer进行数据更新之后执行swap buffer切换前后台buffer。另外由于使用了多读单写锁,多个角色可以通过动画线程同时对动画数据进行读取并计算新一帧骨骼动画。
骨骼兼容
关于骨骼动画计算的所有计算逻辑都作为Anim Graph的一个节点存在:
由于Perception Neuron能够捕捉的关节点有59个,但是要驱动的模型的骨骼数量可能跟Neuron的关节点数量不一致(或多或少)。这个我们需要做特殊的处理, 于是我在NewPoseCalc中做了一个Retargeting:
动画计算
为了能够精确呈现Neuron穿着者的身高及身体各部比例,Neuron可以发送带有精确骨骼位置的动画数据。也就是说我的动画计算不仅仅简单计算骨骼的旋转,还需要计算骨骼的位置移动。整个动画的计算逻辑比较复杂,需要考虑的情况比较多,在这里我们把问题最简化来说一下基本的动画计算。
Neuron捕捉的数据是按照所有骨骼的局部坐标系的初始方向都与世界坐标轴一致来采集的:
在Unity Plugin的文档中要求动捕数据驱动的骨骼模型的初始局部坐标系朝向要和上图一致,这样的话只需要将每根骨骼的新旋转数据赋给对应骨骼即可正确地驱动动画。但是按照这样的要求我没有办法重用现有的大量骨骼模型:
如上图所示,我使用的是Unreal自带的资源”Mannequin“。从图中可以看出每根骨骼的初始局部坐标系的朝向各异。
针对这种资源我简单思考了一下,发现这个限制是可以通过数学计算得以解决的。为了计算方便,所有的动画计算通通都在世界空间下进行计算,然后在将新的骨骼旋转和位置信息设置给对应的骨骼之前变换到每根骨骼的父骨骼空间下表示。
旋转:
FinalRotation = ParentOrigWorldRotation.Inverse() * NewLocalRotation * OrigWorldRotation;
位置:
FinalPosition = (StandardAxis.Inverse() * ParentOrigWorldRotation).Inverse() *NewLocalPosition;
实际的计算过程要比上面的伪代码展示的复杂的多。因为动捕数据的骨骼初始方向跟世界坐标系重合,所以NewLocalRotation等同于骨骼在世界坐标系中的旋转值。位置计算的伪代码加粗的部分是为了适配各种骨骼局部坐标系初始状态带有旋转的模型做的。其中StandardAxis的定义为动捕数据所在的世界空间轴。
跨平台兼容
Unreal插件做了多平台的兼容(Android、IOS、Mac、Windows)。我在三星的S6上做了一个小Demo来演示执行效果。这应该算是Perception Neuron唯一的一个移动端演示了吧。
写在最后
由于现在公司的事情非常多,我很难分出时间继续对这份代码进行更新。跟戴博士商量之后决定将这个插件的代码交给Noitom做后续的更新和维护。
在Unreal Plugin开发的过程中跟Epic资深工程师王祢进行了很多交流。王祢给了我很多建议,还帮我改了一部分功能,让插件使用起来更加友好。我们一起讨论技术细节,研究未来版本的插件的功能改进。跟王祢交流一直都感觉非常愉快,很多问题我都能跟他轻易达成共识,他的技术能力很强,并且对技术有着极高的热情,这些都让我非常的钦佩。
目前Notiom携手HTC合作研发的新型手套VR控制器Noitom Hi5已经发布了:
希望这些先进的动捕设备能够为您的VR产品开发带来更多的灵感,创造出更好的交互体验。
如果您对我的文章感兴趣可以扫描下方的二维码关注我的微信订阅号。在那里我会不定期更新我的技术文章。