直接插值矩阵值会有问题,对于非常小的角度,可能无法观察到不准确性,但是长期来看,您将面临问题。即使您对矩阵进行规范化,更大的角度也会使问题在视觉上明显。
2D旋转非常简单,因此您可以在没有旋转矩阵的情况下很好地完成。最佳方法可能是使用四元数,但它们可能更适用于3D变换。
需要采取以下步骤:
1. 计算旋转、缩放和变换值。如果您已经拥有这些值,则可以跳过此步骤。对于2D矩阵变换,保持这些值分开可能是最容易的。
2. 然后对这些值进行插值。
3. 基于计算构建一个新矩阵。
在动画开始时,您必须从第1步计算值,然后在每个帧上应用第2和第3步。
第1步:获取旋转、缩放和变换
假设起始矩阵为S,结束矩阵为E。
变换值只是最后一列,例如
var start_tx = S[0][2];
var start_ty = S[1][2];
var end_tx = E[0][2];
var end_ty = E[1][2];
非扭曲2D矩阵的比例是指该矩阵跨越的空间中任意一个基向量的长度,例如:
Scale for non-skewing 2D matrix 是指非扭曲的二维矩阵的比例,具体为该矩阵所跨越的空间中任意一个基向量的长度。
// scale is just the length of the rotation matrixes vector
var startScale = Math.sqrt( S[0][0]*S[0][0] + S[1][0]*S[1][0]);
var endScale = Math.sqrt( E[0][0]*E[0][0] + E[1][0]*E[1][0]);
最难的部分是获取矩阵旋转值。好消息是,每次插值只需要计算一次。
对于两个2D矩阵,旋转角度可以基于列向量所创建的向量之间的夹角来计算。如果没有旋转,第一列具有值(1,0),代表x轴,第二列具有值(0,1),代表y轴。
通常情况下,矩阵S的x轴位置表示为:
(S[0][0], S[0][1])
而y轴指向方向为:
(S[1][0], S[1][1])
同样适用于任何2D 3x3矩阵,如E。
使用这些信息,您可以仅使用标准向量数学确定两个矩阵之间的旋转角度 - 如果我们假设没有扭曲。
var s00 = S[0][0]/ startScale;
var s01 = S[0][1]/ startScale;
var e00 = E[0][0]/ endScale;
var e01 = E[0][1]/ endScale;
var dp_start = s00*1 + s01*0;
var dp_between = s00*e00 + s01*e01;
var startRotation = Math.acos( dp_start );
var deltaRotation = Math.acos( dp_between );
if(S[0][1]<0) startRotation = -1*startRotation;
var cp_between = s00*e01 - s01*e00;
if(cp_between<0) deltaRotation = deltaRotation*-1;
var endRotation = startRotation + deltaRotation;
在这里,startRotation仅从矩阵的第一个值的acos中计算。然而,第二列的第一个值,即-sin(angle)大于零,则矩阵已顺时针旋转,因此角度必须为负。这是必须要做的,因为acos只会给出正值。
另一种思考方式是考虑叉积s00*e01 - s01*e00,其中起始位置(s00,s01)为x轴,其中s00 == 1且s01 == 0,结束位置(e00,e01)为(S [0] [0],S [0] [1]),创建叉积。
1 * S[0][1] - 0 * S[0][0]
这是S矩阵的第0行第1列。如果该值为负数,则x轴已经向顺时针方向旋转。
对于endRotation,我们需要从S到E的增量旋转角度。这可以通过计算矩阵跨越的向量之间的点积来类似地计算。同样地,我们测试叉积以查看旋转方向是否为顺时针(负角度)。
步骤2:插值
在动画过程中获取新值是微不足道的插值:
var scale = startScale + t*(endScale-startScale);
var rotation = startRotation + t*(endRotation-startRotation);
var tx = start_tx + t*(end_tx-start_tx);
var ty = start_ty + t*(end_ty-start_ty);
第三步构建矩阵
对于每个帧,构建最终矩阵只需将值放入变换矩阵即可。
var cs = Math.cos(rotation);
var sn = Math.sin(rotation);
var matrix_values = [[scale*cs, -scale*sn, tx], [scale*sn, scale*cs, ty], [0,0,1]]
然后您有一个二维矩阵,这对于任何3D硬件加速器来说都很容易输入。
免责声明:其中一些代码已经经过测试,但有些代码未经过测试,因此可能会发现错误。