* “土豆”指的是偶像大师百万现场剧场时光(ミリシタ/MLTD)
这篇文章讲解土豆的摄像机数据。
10月更新:近日在测试玩具的时候,发现 Unity 是直接支持 F-Curve 的(见 KeyFrame
的文档),而且其构造函数已经清楚地说明了值的意义。是我孤陋寡闻了。
首先要清楚的是,摄像机的平移和旋转和模型、动作的一样,都是适用于 Unity 坐标系的。
打开摄像机数据的 asset bundle,里面同样有四个 Imas.CharacterImasMotionAsset
实例 idl、apa、apg 和 cam。同时还有两个用于竖屏(从名字可以看出,tate=縦)的镜头。(由此可以推测,apa 和 apg 是用于 fever 的特写镜头的。)
接着又看到了熟悉的 Curve
数组。但是有点不一样的是,这里的 key_type
都是 FCurve
。property_name
的字段意义太过简单就不讲了。那么 FCurve
类型的 Curve
该怎么插值呢?我一开始也是不知道的,所以谷歌了一下。从 Blender 的手册中我对F曲线有了一个直观的认识。
Although F-Curves are very similar to Bézier Curves, there are some important differences.
For obvious reasons, a property represented by a Curve cannot have more than one value at a given time, hence:
- When you move a control point ahead of a control point that was previously ahead of the point that you are moving, the two control points switch their order in the edited curve, to avoid that the curve goes back in time.
- For the above reason, it is impossible to have a closed F-Curve.
从这段话中,我推测,F曲线就是(三次)贝塞尔曲线在满足时间条件下的特殊情况。
从手册页面中,我们还可以得到一个重要的信息。在 Blender 中,调整F曲线形状用的是控制点(或者直译为句柄,handle);从几种调整方式中可以看出,每个值点一般有三个控制点。(Vector 明显是可以指定离散值然后自动计算的。)其中有一个明显是调整值,那么另外两个呢?根据已有的知识,每个三次贝塞尔曲线段有四个点(两个值点,两个控制点),也就是说在这一段中,每个值有两个点(一个值点,一个控制点)。所以到此我们就可以猜想,F曲线的另外两个点是切线斜率的控制点了,分别控制左边和右边的斜率。虽然从数学角度想,它们也可以用来控制曲率,但这违反直观操作的习惯,而 Blender 不是面向数学家的,所以最有可能是斜率。
接着我们来解读存储的数据。从曲线数据中我们可以看出一些规律。我们取出 Blooming Star 中的镜头数据。这段数据的属性如下:path="CamBase"
,property_type=General
,property_name=focalLength
,key_type=FCurve
。
[0]
float data = 0
[1]
float data = 35.19532
[2]
float data = ∞
[3]
float data = 0
[4]
float data = 5.666667
[5]
float data = 35.19532
[6]
float data = 0
[7]
float data = ∞
[8]
float data = 5.683333
[9]
float data = 64.44691
[10]
float data = ∞
[11]
float data = -6.156005
首先,从数字的大小和增减幅度来看,有几个数字(第0、4、8个)很可能表示的是时间。如果如此的话,那这四些数据可能就是四个数字为一组的了。这个曲线用于焦距的插值,常用的焦距,以 35 mm 镜头为例,在 35 mm 到 60 mm 左右;焦距再小就是拍广角,再大就是拍远距离特写。第1、5、7个数据的范围就大致在这个区间,所以它们可能就是表示曲线该点的值(表示位置的时候就是坐标,以此类推)。那么剩下的两个呢?根据上面的预备知识,可以推测是左边和右边的斜率。合理性?观察一下,数字2(可能是第一个点的左斜率)是无穷,斜率无穷可以表示不存在,也就是这个点是不连续的。之后右斜率为0,下一个点左为0右不存在,再下一个左不存在右为-6.15。连续性匹配了,而且这些斜率(和标准视频)对比一下还挺合理的。因此就应该是左右斜率没错。
你可能要问,如果是真的不连续,那么两个相邻的曲线点的时间应该是一样的啊。不过你仔细看一下,5.666667和5.683333,考虑到单精度浮点数的精度,相差正好是,这就是上一帧和下一帧嘛。
含义问题解决了,现在来看看怎么插值。我们有的数据是斜率,那么怎么找到两个控制点呢?从一份讲义(第26页)上可以知道,三次贝塞尔曲线(、、、)两端的斜率(也就是在该点的左/右导数)分别是 和 。这样控制点就很容易算出来了……等等,知道了斜率,还需要知道长度才能计算控制点位置。经过实验,我发现这个长度不是常数,应该是与区间长度(时间)有关。最后取的长度是 。
土豆的摄像机和 MMD 的略有不同。定位方式是小问题,前者用的是 eye+target,后者用的是 eye+orientation。最大的问题和身体动作一样,土豆的各属性插值曲线是独立的,MMD 又㕛叒叕是合在一起的。所以还是老方法,逐帧计算。在转换的过程中可能还有额外的变换(估计是因为我用 OpenTK),详见代码。结果可能还有一两个小错误,有时间再推敲一下?
需要注意的是,VMD 只支持整数 FOV,而逐帧计算必然会用到浮点 FOV,所以镜头导出到 VMD 不是一个好的选择。MVD(MMM 用的改进的 VMD)支持浮点 FOV,所以我默认选择生成的是 MVD。所以如果想看土豆的镜头,只能用 MMM 而不能用 MMD。