Повернуть корень анимации BVH вокруг оси Y на угол

Даже если вы не знакомы с форматом файлов BVH, возможно, вы могли бы помочь мне в том, как объединить два поворота.

Я просто беру поворот корня (копируя значения x,y,z) из файла BVH, чтобы каналы корня были установлены следующим образом:

CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation

И я хочу, чтобы поворот корня на несколько градусов мог быть от 0 до 360, вокруг оси y (0,1,0). Это вращение работает в некоторых кадрах. Тем не менее, на большинстве кадров рут странно вращается (это похоже на то, что произошла блокировка карданного подвеса).

Это код, который я использую. C# в Unity. Я просто хочу правильно применить вращение корня вокруг оси Y.

Я могу подтвердить, что код работает нормально (корректная анимация BVH) при добавлении вращения на 0 градусов вокруг оси y. Таким образом, ошибка должна заключаться в комбинации этих двух поворотов (qBefore и YawRotationQuaternion).

private Vector3 getFinalRootRotation(Vector3 rotationFromBvh, float angle)
{
    // [INITIALIZE]: Convert Euler to Quaternion for each axis. 
    Quaternion qX = Quaternion.AngleAxis(rotationFromBvh.x, Vector3.right);
    Quaternion qY = Quaternion.AngleAxis(rotationFromBvh.y, Vector3.up);
    Quaternion qZ = Quaternion.AngleAxis(rotationFromBvh.z, Vector3.forward);
    Quaternion qBefore = qY * qX * qZ; // Multiply them in the correct order.
                                       // Order in BVH file is ZXY.

qY*qX*qZ имеет те же результаты, что и эйлер x,y,z Quaternion.Euler(rotationFromBvh)

    // [CREATE ROTATION AROUND Y]: Get the quaternion of rotating around y axis.
    Quaternion YawRotationQuaternion = Quaternion.AngleAxis(angle, Vector3.up);

    // [APPLY ROTATION AROUND Y]
    Quaternion qAfter = qBefore * YawRotationQuaternion;

    // [RETURN IN XYZ FORM]
    return qAfter.eulerAngles;
}

Моя цель - создать новый BVH с новым поворотом корня. Это часть кода, где я пишу новый поворот в файле. Этот код протестирован и создает файл BVH нормально.

private string CreateMLine(Vector3 rootPosition, Vector3 rootRotation, List<Vector3> rotations)
{
    StringBuilder s = new StringBuilder();
    // Apply root's translation and rotation.
    s.Append(rootPosition.x + " " + rootPosition.y + " " + rootPosition.z + " ");
    s.Append(rootRotation.z + " " + rootRotation.x + " " + rootRotation.y + " ");

    for(int i=1; i<rotations.Count; i++) // start at 1 ==> skip rotation of the root.

    {
        s.Append(rotations[i].z + " " + rotations[i].x + " " + rotations[i].y + " ");
    }
    s.Append("\n");
    return s.ToString();
}

2 ответа

Я бы сказал, что вы получаете значения BVH в порядке Zrotation Xrotation Yrotation по уважительной причине.

Из Quaternion.eulerAngles:

Вращение, которое вращает euler.z градусов вокруг оси z, euler.x градусов вокруг оси x и euler.y градусов вокруг оси y (в этом порядке).

Так что я думаю, что ошибка заключается в

Quaternion qBefore = qY * qX * qZ;

который использует неправильный порядок.


скорее должно быть

Quaternion qBefore = qZ * qX * qY;

Я бы предложил вместо этого использовать Quaternion.Euler

Возвращает вращение, которое вращает z градусов вокруг оси z, x градусов вокруг оси x и y градусов вокруг оси y.

Quaternion qBefore = Quaternion.Euler(rotationFromBvh.x, rotationFromBvh.y, rotationFromBvh.z);

Физически умножение кватернионов означает применение поворота справа налево.

q1 * q2 означает поворот на q2, а затем на q1

Поэтому, если вы хотите применить вращение после другого, поместите его с левой стороны.

В вашем случае это должно быть:

      Quaternion qAfter = YawRotationQuaternion * qBefore;
Другие вопросы по тегам