0

我想在 unity3d 中的三个不同轴(偏航/俯仰/滚动)上以不同速度进行两次旋转,并尝试使用Quaternion.LookRotation().

Quaternion.LookRotation()将方向向量作为第一个参数,所以我认为我可以先调整方向,然后用 lerped 向上向量查看它。

应该没问题Vector3.lerp(),但在这种情况下,我需要在两个轴(X 和 Y)上相对于初始方向以不同的速度调整方向。

例如,我有一个面向目标的相机,然后目标向上和向右移动一点,现在我希望相机也慢慢向右倾斜,但要快一点到目标位置(保持自己的位置) .

如何在两个轴上使用不同速度的方向矢量来使用它Quaternion.LookRotation()

编辑: 将标题从“在 X/Y 上具有不同速度的 Vector3 之间的 Lerp”更改为“偏航/俯仰/滚动具有不同速度的四元数 lerp”并修改了问题以匹配主题。

4

3 回答 3

0

CjLib 的作者在这里。

听起来您实际上不需要摆动扭曲分解。我会说只是得到当前四元数和所需四元数的分解偏航/俯仰/行。然后根据您希望它们单独跟踪目标值的速度更新偏航/俯仰/行值,并从该组偏航/俯仰/行值生成更新的四元数。

使用最大速度上限(我称之为“寻求”)的 Lerping 可能很好,但看起来并不顺畅。我建议使用临界阻尼数值弹簧。这是我在这个主题上写的一个由 3 部分组成的系列的一个无耻的位置。

于 2018-10-18T23:43:39.383 回答
0

多亏了 minorlogic 和CjLib,我尝试了以下方法:

public Quaternion QuaternionLerpOn3Axis(
    Quaternion rot1,
    Quaternion rot2,
    Vector3 lerpSpeed
) {
    if (rot1 != rot2) {
        float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
        float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
        float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
        // Lerp up direction
        Vector3 vecUp = Vector3.Slerp(
            rot1 * Vector3.up,
            rot2 * Vector3.up,
            lerpSpeedRoll
        );
        // Get new rotation with lerped yaw/pitch
        Quaternion rotation = QuaternionUtil.Sterp(
            rot1,
            rot2,
            rot1 * Vector3.right,
            lerpSpeedYaw,
            lerpSpeedPitch,
            QuaternionUtil.SterpMode.Slerp
        );
        // Look at new direction and return rotation
        return Quaternion.LookRotation(
            rotation * rot1 * Vector3.forward,
            vecUp
        );
    } else {
        return rot1;
    }
}

要在不下载 CjLib 的情况下尝试此操作,以下是整个代码,包括用于解码摆动/扭曲的相关部分:

public Quaternion QuaternionLerpOn3Axis(
    Quaternion rot1,
    Quaternion rot2,
    Vector3 lerpSpeed
) {
    if (rot1 != rot2) {
        float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
        float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
        float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
        // Lerp up direction
        Vector3 vecUp = Vector3.Slerp(
            rot1 * Vector3.up,
            rot2 * Vector3.up,
            lerpSpeedRoll
        );
        // Get difference between two rotations
        Quaternion q = rot2 * Quaternion.Inverse(rot1);
        // Decompose quaternion into two axis
        Quaternion rotYaw;
        Quaternion rotPitch;
        DecomposeSwingTwist(
            q,
            rot1 * Vector3.right,
            out rotYaw,
            out rotPitch
        );
        // Lerp yaw & pitch
        rotYaw = Quaternion.Slerp(Quaternion.identity, rotYaw, lerpSpeedYaw);
        rotPitch = Quaternion.Slerp(Quaternion.identity, rotPitch, lerpSpeedPitch);
        // Look at new direction and return rotation
        return Quaternion.LookRotation(
            rotPitch * rotYaw * rot1 * Vector3.forward,
            vecUp
        );
    } else {
        return rot1;
    }
}
public static void DecomposeSwingTwist(
    Quaternion q,
    Vector3 twistAxis,
    out Quaternion swing,
    out Quaternion twist
) {
    Vector3 r = new Vector3(q.x, q.y, q.z); // (rotation axis) * cos(angle / 2)
    float Epsilon = 1.0e-16f;

    // Singularity: rotation by 180 degree
    if (r.sqrMagnitude < Epsilon) {
        Vector3 rotatedTwistAxis = q * twistAxis;
        Vector3 swingAxis = Vector3.Cross(twistAxis, rotatedTwistAxis);

        if (swingAxis.sqrMagnitude > Epsilon) {
            float swingAngle = Vector3.Angle(twistAxis, rotatedTwistAxis);
            swing = Quaternion.AngleAxis(swingAngle, swingAxis);
        } else {
            // More singularity: rotation axis parallel to twist axis
            swing = Quaternion.identity; // no swing
        }

        // Always twist 180 degree on singularity
        twist = Quaternion.AngleAxis(180.0f, twistAxis);
        return;
    }

    // Formula & proof: 
    // http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/
    Vector3 p = Vector3.Project(r, twistAxis);
    twist = new Quaternion(p.x, p.y, p.z, q.w);
    twist = Normalize(twist);
    swing = q * Quaternion.Inverse(twist);
}
public static Quaternion Normalize(Quaternion q) {
    float magInv = 1.0f / Magnitude(q);
    return new Quaternion(magInv * q.x, magInv * q.y, magInv * q.z, magInv * q.w);
}
public static float Magnitude(Quaternion q) {
    return Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
}

到目前为止,这是我可以在三个不同轴上实现具有不同速度的四元数(s)lerp 并获得合理可接受的结果的唯一方法。

但在我看来,这不是一个真正的数学解决方案,如果 lerp 值低于 ~1.5f(尤其是 Z/Roll 轴),它就不能很好地工作,而且开销很大。

任何想法如何用更少/更好的代码解决这个难题?

于 2018-10-09T12:25:29.073 回答
0

...另一种方法:

现在我尝试将分解摆动/扭曲的概念扩展到分解偏航/俯仰/滚动。

如果目标没有翻转 180°,这可以正常工作(?),并且它仍然需要来自真正知道如何处理四元数旋转的人的一些输入/反馈。

public Quaternion QuaternionLerpYawPitchRoll(
    Quaternion rot1,
    Quaternion rot2,
    Vector3 lerpSpeed
) {
    if (rot1 != rot2) {
        float lerpSpeedPitch = lerpSpeed.x * Time.deltaTime;
        float lerpSpeedYaw = lerpSpeed.y * Time.deltaTime;
        float lerpSpeedRoll = lerpSpeed.z * Time.deltaTime;
        // Decompose quaternion into yaw/pitch/roll
        Quaternion rotYaw;
        Quaternion rotPitch;
        Quaternion rotRoll;
        DecomposeYawPitchRoll(rot1, rot2, out rotYaw, out rotPitch, out rotRoll);
        // Lerp swing & twist
        rotYaw = Quaternion.Slerp(Quaternion.identity, rotYaw, lerpSpeedYaw);
        rotPitch = Quaternion.Slerp(Quaternion.identity, rotPitch, lerpSpeedPitch);
        rotRoll = Quaternion.Slerp(Quaternion.identity, rotRoll, lerpSpeedRoll);
        // Combine yaw/pitch/roll with current rotation
        return Quaternion.LookRotation(
            rotPitch * rotYaw * rot1 * Vector3.forward,
            rotRoll * rot1 * Vector3.up
        );
    } else {
        return rot1;
    }
}
public static void DecomposeYawPitchRoll(
    Quaternion rot1,
    Quaternion rot2,
    out Quaternion yaw,
    out Quaternion pitch,
    out Quaternion roll
) {
    Vector3 pitchAxis = rot1 * Vector3.right;
    Vector3 rollAxis = rot1 * Vector3.forward;
    Vector3 yawAxis = rot1 * Vector3.up;
    // Get difference between two rotations
    Quaternion diffQ = rot2 * Quaternion.Inverse(rot1);

    Vector3 r = new Vector3(diffQ.x, diffQ.y, diffQ.z); // (rotation axis) * cos(angle / 2)
    float Epsilon = 1.0e-16f;

    // Singularity: rotation by 180 degree
    if (r.sqrMagnitude < Epsilon) {
        Vector3 rotatedPitchAxis = diffQ * pitchAxis;
        Vector3 rotatedYawAxis = Vector3.Cross(pitchAxis, rotatedPitchAxis);
        Vector3 rotatedRollAxis = diffQ * rollAxis;

        if (rotatedYawAxis.sqrMagnitude > Epsilon) {
            float yawAngle = Vector3.Angle(pitchAxis, rotatedPitchAxis);
            yaw = Quaternion.AngleAxis(yawAngle, rotatedYawAxis);
        } else {
            // More singularity: yaw axis parallel to pitch axis
            yaw = Quaternion.identity; // No yaw
        }
        if (rotatedRollAxis.sqrMagnitude > Epsilon) {
            float rollAngle = Vector3.Angle(yawAxis, rotatedYawAxis);
            roll = Quaternion.AngleAxis(rollAngle, rotatedRollAxis);
        } else {
            // More singularity: roll axis parallel to yaw axis
            roll = Quaternion.identity; // No roll
        }

        // Always twist 180 degree on singularity
        pitch = Quaternion.AngleAxis(180.0f, pitchAxis);
    } else {
        // Formula & proof: 
        // http://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/
        pitch = GetProjectedRotation(diffQ, pitchAxis);
        roll = GetProjectedRotation(diffQ, rollAxis);
        yaw = diffQ * Quaternion.Inverse(pitch);
    }
}
public static Quaternion GetProjectedRotation(Quaternion rotation, Vector3 direction) {
    Vector3 r = new Vector3(rotation.x, rotation.y, rotation.z);
    Vector3 proj = Vector3.Project(r, direction);
    rotation = new Quaternion(proj.x, proj.y, proj.z, rotation.w);
    return Normalize(rotation);
}
public static Quaternion Normalize(Quaternion q) {
    float magInv = 1.0f / Magnitude(q);
    return new Quaternion(magInv * q.x, magInv * q.y, magInv * q.z, magInv * q.w);
}
public static float Magnitude(Quaternion q) {
    return Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
}
于 2018-10-11T17:51:54.947 回答