2 个四元数的 Slerp 与每轴四元数的“s”lerp 的乘法

Slerp on 2 quaternions vs the multiplication of "s"lerp of per-axis quaternions

提问人:Spectraljump 提问时间:9/10/2023 最后编辑:Spectraljump 更新时间:10/20/2023 访问量:44

问:

我将四元数分成 3 个旋转,然后对这些旋转进行插值,然后相乘以合并结果,我得到的结果与球面插值非分裂四元数不同。

var quat_A_full = quaternion.EulerXYZ(ax_rad, ay_rad, az_rad);//-0.3161251f, 0.7749172f, 0.0828281f, 0.5410246f
var quat_B_full = quaternion.EulerXYZ(bx_rad, by_rad, bz_rad);//-0.5004139f, 0.5069054f, 0.1151911f, 0.6923611f

var quat_A_x = quaternion.RotateX(ax_rad);//0.2424405f, 0f, 0f, 0.9701663f
var quat_A_y = quaternion.RotateY(ay_rad);//0f, 0.8527206f, 0f, 0.5223673f
var quat_A_z = quaternion.RotateZ(az_rad);//0f, 0f, 0.5134861f, 0.8580979f

var quat_B_x = quaternion.RotateX(bx_rad);//-0.7160885f, 0f, 0f, 0.6980096f
var quat_B_y = quaternion.RotateY(by_rad);//0f, 0.4602496f, 0f, 0.8877895f
var quat_B_z = quaternion.RotateZ(bz_rad);//0f, 0f, -0.3183014f, 0.9479896f

var quat_A_full_reconstructed = math.mul(quat_A_z, math.mul(quat_A_y, quat_A_x);
// quat_A_full == quat_A_full_reconstructed <-- this is TRUE

var quat_B_full_reconstructed = math.mul(quat_B_z, math.mul(quat_B_y, quat_B_x);
// quat_B_full == quat_B_full_reconstructed <-- this is also TRUE

// but if I start to interpolate, sometimes I get results drifting off quite lot. 

var result_full = math.slerp(quat_A_full, quat_B_full, animation);

var result_parts = math.mul(
    math.slerp(quat_A_z, quat_B_z, animation), 
    math.mul(math.slerp(quat_A_y, quat_B_y, animation), 
             math.slerp(quat_A_x, quat_B_x, animation))
);

result_full != result_parts // it drifts by a lot sometimes:
(-0.4088441f, 0.6605082f, 0.09956253f, 0.621822f)
[ x: -107.9114542, y: 47.7336353, z: 80.7913939 ] (euler degrees)
vs
(-0.2624312f, 0.6446596f, 0.2598185f, 0.6693567f)
[ x: -87.4064322, y: 46.6058942, z: 87.1785867 ] (euler degrees)

由于动画限制,我需要对每个轴进行插值。也许这是不可能的?

PS:这是我使用的(unity的)四元数slerp函数:

public static quaternion slerp(quaternion q1, quaternion q2, float t)
    {
        float dt = dot(q1, q2);
        if (dt < 0.0f)
        {
            dt = -dt;
            q2.value = -q2.value;
        }

        if (dt < 0.9995f)
        {
            float angle = acos(dt);
            float s = rsqrt(1.0f - dt * dt);    // 1.0f / sin(angle)
            float w1 = sin(angle * (1.0f - t)) * s;
            float w2 = sin(angle * t) * s;
            return quaternion(q1.value * w1 + q2.value * w2);
        }
        else
        {
            // if the angle is small, use linear interpolation
            return nlerp(q1, q2, t);
        }
    }
C# 数学 向量 元数 lerp

评论

0赞 Spectraljump 9/10/2023
我怀疑当您在 2 个旋转之间插值时,生成的旋转可能会根据所涉及的另一个轴具有不同的方向。即在我的情况下,slerp([x1, 0, 0], [x2,0,0]) 可能与 slerp ([x1,y1,z1], [x2, y2, z2]) 的方向不同。但是,我该如何获得我想要的等效结果呢?
0赞 Spectraljump 9/11/2023
我需要以某种方式在 Q1 和 Q2 之间做一个球形 lerp,但限制在特定轴周围。🤔

答:

0赞 minorlogic 10/20/2023 #1

听起来 STERP 对您有用。

STERP四元数插值

评论

0赞 Spectraljump 10/25/2023
嘿,很棒的文章。但是,如果我理解正确,如以下 gif 所示:allenchou.net/wp-content/uploads/2018/05/slerp-vs-sterp.gif(左边是 slerp,右边是摆动和扭转的 sterp),那么 sterp 就不是球形的。因此,在精度方面,对于动画来说,它相当于线性插值;所以这就是我试图避免的。
0赞 minorlogic 11/30/2023
它使用球面插值。它使用分解 2 旋转和 SLERP 分别进行。听起来就像你在搜索一样。