【问题标题】:Tweening a rotation with quaternions使用四元数补间旋转
【发布时间】:2013-12-24 08:07:53
【问题描述】:

在 Actionscript 3 中,我有一个 3D 对象,它围绕它的 x、y 和 z 轴随机旋转。我希望能够将其停在原地,然后将其旋转到指定的旋转之间,并采用最短的路线。似乎四元数是这项工作的正确工具,但我不确定如何正确补间。我正在尝试补间轴角旋转,然后将其转换为四元数。到目前为止,这是我的测试代码。

var startV3DV:Vector.<Vector3D> = new <Vector3D>[positionV3D, new Vector3D(0,1,0,0), scaleV3D];
var endV3DV:Vector.<Vector3D> = new <Vector3D>[positionV3D, new Vector3D(0,1,0,Math.PI), scaleV3D];
var recomposeV3DV:Vector.<Vector3D> = new <Vector3D>[positionV3D, new Vector3D(0,0,0,0), scaleV3D];

TweenMax.to(startV3DV[1], 2, {x:endV3DV[1].x, y:endV3D[1].y, z:endV3D[1].z, w:endV3D[1].w, onUpdate:quat});

var quatV3D:Vector3D = new Vector3D();
function quat():void {
    quatV3D.x = currentV3D[1].x * Math.sin(currentV3D[1].w * .5);
    quatV3D.y = currentV3D[1].y * Math.sin(currentV3D[1].w * .5);
    quatV3D.z = currentV3D[1].z * Math.sin(currentV3D[1].w * .5);
    quatV3D.w = Math.cos(currentV3D[1].w * .5);
    recomposeV3DV[1] = quatV3D;
    myMatrix3D.recompose(recomposeV3DV,"quaternion");
}

它实际上将对象围绕其 y 轴的旋转补间了一段时间,但是当它达到 PI/2 时,它会出错,并且我在recompose line,我认为这意味着 quatV3D 属性之一不适合四元数公式。有关如何正确执行此操作的任何想法?

【问题讨论】:

    标签: actionscript-3 3d rotation quaternions


    【解决方案1】:

    我无法使用您提供的数据重现它。但您似乎在使用 currentV3D 矢量时在 startV3DV 上进行补间,您的示例代码中没有提及。它可能包含无效数据,例如 0 轴或 0 刻度。

    此外,这不是四元数用于补间的方式。您似乎在这里尝试做的是在两个轴/角度表示之间进行补间,然后在每次更新时从中生成一个四元数。您不妨直接使用内置的“axisAngle”旋转模式进行重构。这可能不会达到您的预期,因为 1) 不能保证采用最短角度,以及 2) 当轴不同时 - 关于插值的外观,所有的赌注都没有;)

    您应该做的是将开始和结束旋转转换为四元数,就像您当前在 quat() 中所做的那样,然后在它们之间进行插值。您仍然需要更正最短路径:如果两个四元数之间的 4 分量点积

    如果你想坚持 TweenMax,你仍然可以这样做(可能包含一些粗心的错误,但你应该明白要点):

        t = 0;
        TweenMax.to(this, 2, {t:1, onUpdate:lerp});
    
        function lerp() : void
        {
            var x:Number, y:Number, z:Number, w:Number;
            var w1:Number = start.w, x1:Number = start.x, y1:Number = start.y, z1:Number = start.z;
            var w2:Number = end.w, end:Number = end.x, y2:Number = end.y, z2:Number = end.z;
            var len:Number;
    
            // shortest direction
            if (x1*x2 + y1*y2 + z1*z2 + w1*w2 < 0) {
                x2 = -x2;
                y2 = -y2;
                z2 = -z2;
                w2 = -w2;
            }
    
            x = x1 + t*(x2 - x1);
            y = y1 + t*(y2 - y1);
            z = z1 + t*(z2 - z1);
            w = w1 + t*(w2 - w1);
    
            len = 1.0/Math.sqrt(x*x + y*y + z*z + w*w);
    
            quatV3D.x = x*len;
            quatV3D.y = y*len;
            quatV3D.z = z*len;
            quatV3D.w = w*len;
    
            recomposeV3DV[1] = quatV3D;
            myMatrix3D.recompose(recomposeV3DV,"quaternion");
        }
    

    您可能还想研究球形线性插值(“slerp”),与线性插值不同,它们具有恒定的角速度。我就偷懒了,直接给你看我几年前写的Away3D四元数类:https://github.com/away3d/away3d-core-fp11/blob/008b1d83d3330281034b90ca7072722a9f486958/src/away3d/core/math/Quaternion.as

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      事实证明 Matrix3D 已经可以做使用四元数的 LERP:

      var startM3D:Matrix3D = myObject.transform.matrix3D.clone();
      var endM3D:Matrix3D = new Matrix3D(new <Number>[-1.2000000476837158,0,1.0490733615142744e-7,0,0,1.2000000476837158,0,0,-1.0490733615142744e-7,0,-1.2000000476837158,0,220,150,0,1]);
      var currentM3D:Matrix3D = new Matrix3D();
      
      var t:Number = 0;
      TweenMax.to(this, 4, {t:1, onUpdate:lerp});
      
      function lerp():void {
          currentM3D = Matrix3D.interpolate(startM3D, endM3D, t);
          myObject.transform.matrix3D = currentM3D;
      }
      

      补间看起来不错。不过我也会试试你的 SLERP。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-14
        • 2020-04-11
        相关资源
        最近更新 更多