🤖 机器人眼睛动画的实现
!本篇文章过于久远,其中观点和内容可能已经不准确,请见谅!~
想分享的是从脑袋里有一个想法,到调研、遇到瓶颈、找到突破、走弯路、实现还算可以的效果,最后完善到完美。就像大部分的工作一样,每一步都好像都不太顺利,但是最后还是能够把问题解决。

又想到之前了解到的一个小机器人:vector/cozmo,很好玩,但是价格、本地化不太友好:

又又想到了蔚来汽车的 Nomi:
感觉表情这个东西特别有趣,自己如果实现一个感觉会挺有意思的,心想偌大的互联网世界总该有类似的项目吧。
简单搜了一下,关键词从
robot face、robot emotion、robot expressions、lcd-like robot face 搜了一大堆,可以说可以用的代码、矢量图等东西没有能让我直接使用的,有的话也是极其粗糙,所以随便做一个玩玩的想法逐渐接受了需要做点脏活累活了。决定好好调研下 anki 的相关方案作为突破口,还是比 Nomi 这种资料都很少的项目和 K-VRC 那种纯影视实现的靠谱点的,我还真就不信真心想做的这个东西没办法实现?
花了大力气找到的最高分辨率:

表情十分丰富
Vector 社区提了很多:https://randym32.github.io/Anki.Vector.Documentation/tools/Eye%20animation.html 相关周边知识,以为会有点什么有用的,但是一点都没,只有一些新闻稿中的配图。找到了一些 SDK,也只是可以在屏幕上显示什么。

从找到的工程师的图也佐证了,确实用的这些工具:

早期他们的设计:

手写的很多场景和脚本设计:

还找到了他们公司用
joysticksnsliders 之类的 AE 工具?模拟做的一个教学视频,实现动态可调的表情(和我们要找的代码级实现完全不同):结论就是:
从各种拼凑的知识碎片中了解到,他们有来自皮克斯、梦工厂专门的动画团队,做了上千个预设场景(看到这个文字的时候觉得有点夸张,但是真实看到 1186 个动画文件的时候,内心只有敬仰了),各种各样的表情。也就是说那些非常好玩的表情和反应,其实都是预设的整套动作,确实有 AI 相关的东西触发这些一串的动作。开发动画用的是 maya,有专门的 GUI 的表情控制工具。
Vector 的表情不是随机的,也不是简单几个情绪表情的加权组合,是根据各种故事情节编排实现的一个个表情帧,手动复刻这个能力的成本实在是太高了。
以上都是我在感兴趣调研过程中查到的资料啥的,很有限,不过已经开始想上手做了。
在没深入了解之前,我的计划是大概做一个眼睛的形状,根据不同的情绪实现不同的形状,然后手动切换情绪即可实现不同的表情,顶多在切换的时候做点补间动画。
简单搜了下,强烈建议看下这个网页,能够告诉你捏人的业务能够做的多详细(18y+):
里面的眼睛形状不考虑方向和眨眼,有 25 种个示例:

嘴型的区别甚至达到了 80 种。
如果要实现比较丰富的表情,达到灵动的感觉,能够预料到肯定有太多的
dirty work 要做了,更何况我也不是很懂画画,作为一个 side project,如果这么做的话,大概率是要烂尾,和我那些虎头蛇尾的项目一起落灰了。所以调研了挺久,也一直没新建文件夹。
之前 Vector 这个项目是Anki 这个公司众筹做的,但是这个公司最近被 DDL(digital-dream-labs) 公司收购。翻了下他们的 Twitter,本来想找找有什么高清大图之类的参考,但是意外看到他们现在仍然在众筹的页面,然后在最近的更新中发现了一个很重要的信息:

虽然是仅支持者可看,但是简单的搜一下发现内容被放到了 GitHub 上。
真是非常难得,翻了一下居然是一些原生的动画文件:
包括整个组织下面的开源项目,包括 build 的很多代码都开了源,甚至有详细的安装教程。
如果想要复刻完整的机器人逻辑,实话实话也确实不可能。但是我只是想实现怎么画出来这些,所以不要求太多。
翻了翻这个动画资源仓库,找到了一些能开始工作的点:
- 虽然是用 maya 才能上手的项目,但是里面包含了很多的控制数据,大部分是动画部分。
- 里面翻到了一些 pose.json 的文件,包含了控制参数和预览图。
- 大概 34 个表情相关的 pose。
- 里面的控制参数包括了:位移、缩放、发光、旋转、亮度、圆角等
- 一些比较特别的效果直接用的序列帧做的
有了这些控制帧之后大概了解了怎么实现,但是还不太清楚这些参数怎么拼在一起,比如眼睛多大、原始位置、应用在什么形状上之类。
官方教程因为是纯英文的,而且看得出来整个项目经历了很多的“变故”,
maya 2016、maya 2018 软件都混着用。在 maya 2018 打开失败后又装了 maya 2016 ,按照安装流程装了插件,修改 env 文件,最后能够打开了项目的场景,能够预览到他们的项目了:
粗略看了下,因为对 maya 用的不熟悉,所以了解到的也很有限:
整个 maya 负责很多场景的设计和导出,控制了动作参数什么的。动画师设计了脚本和场景,然后在 maya 里面制作动画,然后导出控制参数,最后 Robot 机器本体根据参数复现行为和显示。
眼睛的 LED 部件比较特殊,虽然在 maya 中看得到,但是和实际的画面不同,需要额外的代码才能渲染到屏幕上。
也就是上面那些眼睛的曲线只是参数示意。
不过能看到有哪些控制元素,包括:眼睛、上下眼睑、四个圆角、瞳孔,这些元素都能控制位移、缩放、旋转等基本参数,形成最后效果。
maya 不会用,好在项目中有一些模型,fbx、obj 之类的文件。之前对 Three.js 用的比较多,所以简单搭了个架子就开始上手预览,去找自己想要的东西了。
其中眼睛部分单独过滤后,分解下形状:
从前面看的话,能够大概看到整个眼睛的效果了:
看着挺好玩,但就像上面说的,这些都是模型图,最开始的问题还是没解决,怎么把参数实现成图像?
好在他们开源了不止这一个动画文件本身,他们还把主要的代码仓库 开源 了,包括了怎么画面部的:
项目本身用到了 openCV(上次用到还是在本科的时候),很强大的一个计算机视觉库,虽然已经封装的很好了,但是到处都是 Matrix 和 Vector 还是着实懵了一阵子。
所以...直接无脑复刻整个绘制逻辑不就行了吗?而且不就是 CPP 嘛?不就是矩阵嘛?不就是向量运算嘛?不就是 openCV 嘛?都写过的东西,在 TypeScript 中复刻整个逻辑不会很难吧,开干!
花了一些时间,用了 opencv-ts、tstl、tsm 等第三方库,加上很多魔改,终于把代码从 C++ 转到 TypeScript 下了,因为只是代码级别的移植,也都是图像级别的数学计算,里面的逻辑不用考虑太多。
说实话这个时候,我的内心不是长舒一口气,而是很担心,因为我知道这种代码移植不可能一次成功,尤其涉及到周边其他文件的逻辑,毕竟原代码我没跑起来,所以更需要很多调试,甚至一个运算错误都要排查很久,某一个矩阵相乘的正确性出错了都很难定位到。
所以考虑了一下,决定不花费这么大精力去调试了。即使我可以一个个函数的调试,甚至可能半个小时调试成功,最终能够 100% 复刻,但是这个 side project 我不太想赌这个时间了,如果有人给钱还行,如果再要一天甚至更长时间都不一定调试出来,成本太高。
别人的核心逻辑不加思考直接搬运,在某些场景可以实现效果,但是如果逻辑比较复杂、黑盒缕不出来脉络的话,最终交付会出问题的,不管怎么样源码还是要读一读的。
其实看到之前的 pose.json 已经大概知道有哪些控制了,就是不太清楚这些控制参数施加在那些元素上,原始逻辑也都是矩阵运算,并不是很清晰。
不过...不就是猜嘛?!
{"metadata": "...","objects": {"x:eyeCorner_L_innerBtm_ctrl": {"attrs": {"scaleX": { "type": "double", "value": 0.9363065595417932 },"scaleY": { "type": "double", "value": 1.0393205391389768 }}},"x:mech_eye_R_ctrl": {"attrs": {"translateX": { "type": "doubleLinear", "value": 0.0 },"translateY": { "type": "doubleLinear", "value": 0.0 },"GlowSize": { "type": "double", "value": 0.5 },"scaleX": { "type": "double", "value": 1.0434263501720864 },"scaleY": { "type": "double", "value": 1.0434263501720864 },"rotateZ": { "type": "doubleAngle", "value": 0.0 },"Lightness": { "type": "double", "value": 1.0 }}},// ...}
位移旋转什么的基本上都能够知道了,在代码移植的过程中基本上都知道各个参数的意义了。
阅读了两遍原始的绘制代码,也基本上知道了图像形状、圆角、位置之类的实现。
上面我们就意识到了,那些曲线只是示意图,但是却提示我们大致各个部位的位置。
至于用 css、svg、canvas 都是绘制的不同实现,刚开始先用最容易实现的 div + css 搭出来。
<div class="eye-panel"><div class="eye left-eye"><div class="pupil"></div><div class="upperLid"></div><div class="lwrLid"></div></div><div class="eye right-eye"><div class="pupil"></div><div class="upperLid"></div><div class="lwrLid"></div></div></div>
几个关键的点:
- css border-radius 支持椭圆弧
- 部分参数的设置区分左右,配置文件的左右和视觉的左右相反
- 很多数据不是在一个项目里面,所以找起来很麻烦
- 参数的应用多试试就行
- 原项目的绘制都是定位做的,所以为了容易调试,也都没用 flex 之类的布局
- 定位可以使用 absolute + top、left 或者 transform: translate,后者性能更好但是 scale 和 translate 等效果会叠加
- overflow: hidden; 的实现在部分浏览器上存在边界露边问题,可以用 clip-path 代替(所以眼睑的实现不用div,而是用 clip-path 的样式直接切出来,上面的 html 结构也因此改变了)
一点源码阅读,加上一点点细节(各种 CSS: border-radius、position 和 transform、clip-path):
太有意思了,过程中花费了很多的心思,实现效果因为不优雅,也改了三四版整个代码,不太想将就嘛。
不想在实现细节上浪费太多口舌,也无意做手把手教程什么的,所以代码几乎没贴,有感兴趣的可以联系我一起交流,虽然很喜欢开源精神,但是伸手党那么多的现在,有意思的个人项目还是捂着吧,如果哪天开心了再开源出来。
原版相比还有随机噪点、扫描线、发光和亮度,不过这些都是简单的细节了。
文章行至此处已经啰嗦很长了,实现静态的表情,顶多是切换的时候用 css 的动画实现了转换动画。
后面的原厂动画新开下一篇文章写。
预览可以到 机器人眼睛动画的演示
动画的实现在最后用 SVG 和 Canvas 重构了,所以本篇文章的示例代码看看就行,而且我的大部分文章一般不涉及手把手的代码细节。
感谢您的阅读,本文由 Ubug 版权所有。如若转载,请注明出处:Ubug(https://ubug.io/blog/k-vrc-vector-cozmo)