提问人:Spectraljump 提问时间:9/10/2023 最后编辑:Spectraljump 更新时间:10/20/2023 访问量:44
2 个四元数的 Slerp 与每轴四元数的“s”lerp 的乘法
Slerp on 2 quaternions vs the multiplication of "s"lerp of per-axis quaternions
问:
我将四元数分成 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);
}
}
答:
0赞
minorlogic
10/20/2023
#1
听起来 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 分别进行。听起来就像你在搜索一样。
评论