Плавное вращение с кватернионами

Кватернион может описывать не только вращение, но и ориентацию, то есть вращение из начальной (нулевой) позиции.

Я хотел моделировать плавное вращение от одной ориентации к другой. Я рассчитал стартовую ориентацию startOrientation и конец ориентации endOrientation и хотел бы описать промежуточные ориентации как startOrientation*(1-argument) + endOrientation*argument в то время как argument изменения от 0 в 1,

Код для функции обновления движка обезьяны следующий:

@Override
    public void simpleUpdate(float tpf) {

        if( endOrientation != null ) {

            if( !started ) {
                started = true;
            }
            else {

                fraction += tpf * speed;
                argument = (float) ((1 - Math.cos(fraction * Math.PI)) / 2);

                orientation = startOrientation.mult(1-argument).add(endOrientation.mult(argument));
                //orientation = startOrientation.mult(1-fraction).add(endOrientation.mult(fraction));
                log.debug("tpf = {}, fraction = {}, argument = {}", tpf, fraction, argument);
                //log.debug("orientation = {}", orientation);

                rootNode.setLocalRotation(orientation);

                if( fraction >= 1 ) {

                    rootNode.setLocalRotation(endOrientation);
                    log.debug("Stopped rotating");

                    startOrientation = endOrientation = null;
                    fraction = 0;
                    started = false;
                }
            }
        }


    }

Предполагалось, что формула косинуса будет моделировать плавное ускорение в начале и замедление в конце.

Код работает, но не так, как ожидалось: плавное вращение начинается и заканчивается задолго до fraction а также argument ценности достигают 1 и я не понимаю, почему.

Почему orientation ценность достигает endOrientation так быстро?

1 ответ

Решение

Вы заявили, что в вашем случае startOrientation был изменен. Тем не мение; следующее остается верным

Интерполяция между кватернионами

Метод slerp включен в класс Quaternion для этой цели: интерполяция между двумя вращениями.

Предполагая, что у нас есть два кватерниона startOrientation а также endOrientation и мы хотим, чтобы точка interpolation между ними мы затем интерполируем, используя следующий код:

float interpolation=0.2f;
Quaternion result=new Quaternion();
result.slerp(startOrientation, endOrientation, interpolation);

Почему ваш подход может быть опасным

Кватернионы несколько сложны внутри и следуют несколько иным математическим правилам, чтобы сказать векторы. Вы назвали multiply(float scalar) метод по кватерниону. Внутренне это выглядит так

public QuaternionD mult(float scalar) {
        return new QuaternionD(scalar * x, scalar * y, scalar * z, scalar * w);
}

Так что он просто делает умножение всех элементов. Это явно не возвращает вращение, которое scalar раз размер. Фактически, такой кватернион больше не представляет действительное вращение вообще, так как он больше не единичный кватернион. Если вы позвонили normalise в этом кватерионе это немедленно отменит масштабирование. Я уверен Quaternion#multiply(float scalar) имеет некоторое применение, но я еще не нашел их.

Это также тот случай, когда "добавление" кватернионов не объединяет их. На самом деле вы умножаете их. Таким образом, объединение q1, затем q2, затем q3 будет достигнуто следующим образом:

Quaternion q0 = q1.mult(q2).mult(q3);

Шпаргалка невероятно полезна для этого

Сравнение формулы и slerp

В вашем случае ваша формула для интерполяции почти, но не совсем верна. Это показывает график рыскания для интерполяции между двумя кватернионами, используя оба метода

Другие вопросы по тегам