Ограничить шаг камеры - PullRequest
       44

Ограничить шаг камеры

12 голосов
/ 09 августа 2010

Как эффективно ограничить высоту камеры, если у меня только кватернион камеры? Нужно ли переводить в углы Эйлера, а затем обратно в кватернион или есть какой-то другой способ?

Ответы [ 4 ]

2 голосов
/ 18 августа 2010

Если у камеры никогда нет рулона (как это часто бывает во многих играх, например, в шутерах от первого лица), решение простое.Если есть бросок, то есть дополнительный шаг.Я начну с того, что делать, если нет крена, и обобщу решение о том, что делать, если есть.

Пусть qc будет поворотом камеры.Позвольте qy вращение с тем же рысканием, что и qc, но с нулевым шагом.Если крена нет, вращение камеры - это вращение вокруг оси, за которым следует поворот шага:

qc = qp * qy

Мы можем восстановить вращение шага qp как вращение от qy до qc:

qp = qc * qy^-1

Хитрость заключается в том, чтобы построить qy, поэтому мы можем включить его в вышеприведенное уравнение для решения для qp.Пусть vc будет единичным вектором, указывающим из объектива камеры, или «вектором вперед».Пусть vy будет тем же вектором, но спроецирован на горизонтальную плоскость и нормализован.Наконец, пусть v0 будет прямым вектором, когда поворот камеры qc является единичным поворотом.Вращение, которое вращает v0 в vy, является вращением рыскания.Угол может быть задан как:

yaw = asin(Norm(cross(v0, vy)))

Соответствующее вращение вокруг оси равно:

qy = { cos(yaw/2), up * sin(yaw/2) }

Где «вверх» - единичный вектор в направлении вверх, то есть ось для поворотов рыскания.,Вставьте это в qp = qy ^ -1 * qc выше, чтобы получить кватернион высоты тона qp.Наконец, получим угол тангажа из qp следующим образом:

pitch = 2*asin(Dot(right, [qp[1], qp[2], qp[3]]))

Где «право» - это единичный вектор в правильном направлении, то есть ось для вращения тангажа.

Как я уже говорил, вещиусложняется, если камера также имеет рулон, но общая стратегия та же.Вы формулируете поворот камеры как произведение компонентов вращения, а затем изолируете нужный компонент (в данном случае шаг).Например, если последовательность эйлера, которую вы используете для определения «высоты тона», является обычной последовательностью вращения рыскания-рыскания, вы определяете qc как:

qc = qr * qp * qy

. Мы можем определить переменную qx как объединенную высоту иВращения броска:

qx = qr * qp

Теперь мы можем записать qc как:

qc = qx * qy

Мы уже знаем, как решить для qx в этой форме, повторяя шаги, которые мы использовали выше для решения дляQP.Переставляя определение для qx, мы получаем:

qp = qr^-1 * qx

Мы только что решили для qx, поэтому, чтобы найти вращение основного тона qp, нам нужен только бросок qr.Мы можем построить его, используя векторы, как мы делали ранее.Пусть vc снова будет вектором вперед.Бросок будет вращением вокруг этого вектора.Пусть vu будет вектором работы камеры (в мировых координатах), и пусть vu0 будет вектором работы камеры с нулевым креном.Мы можем построить vu0, спроецировав глобальный вектор вверх на плоскость, перпендикулярную vc, а затем нормализовав.Вращение крена qr - это поворот от vu0 к vu.Ось этого вращения - прямой вектор vc.Угол крена равен

roll = asin(Dot(vc, cross(vu0, vu)))

Соответствующий кватернион равен:

qr = { cos(roll/2), forward * sin(roll/2) }

Где «вперед» - ось вращения крена.

1 голос
/ 17 августа 2010

Шаг - это всего лишь один компонент полного вращения, поэтому, если вы хотите так думать о своем вращении, вам лучше хранить шаг отдельно, возможно, используя углы Эйлера.

Простое преобразование в углы Эйлера и обратно, когда вам нужно ограничить движение, может работать не слишком хорошо, поскольку вам нужно будет помнить последний кадр (если он у вас есть), чтобы увидеть, превысили ли вы предел и в каком направление.

На мой взгляд, основной смысл кватернионов заключается в том, что вам не нужно беспокоиться о подобных ограничениях. Все просто работает без каких-либо особенностей. Почему именно вы хотите ограничить высоту?

0 голосов
/ 04 июля 2014

Я могу немного опоздать на вечеринку, но вот как я это решил:

        // "Up" = local vector -> rotation * Vector3.UnitY
        // "Forward" = local vector -> rotation * Vector3.UnitZ
        // "Right" = local vector -> rotation * Vector3.UnitX

    public void Rotate(Vector3 axis, float angle)
    {
        if (LerpRotation)
        {
            RotationTarget *= Quaternion.FromAxisAngle(axis, angle);
        }
        else
        {
            Rotation *= Quaternion.FromAxisAngle(axis, angle);
        }
        //Locking the Pitch in 180°
        float a = Vector3.CalculateAngle(Vector3.UnitY, Up);
        float sign = Math.Sign(Forward.Y);
        float delta = (float)Math.PI / 2 - a;
        if(delta < 0)
            Rotation *= Quaternion.FromAxisAngle(Right, delta * sign);
    }
0 голосов
/ 10 августа 2010

Кватернионы вращения камеры можно определить как:

vector A = [x, y, z] 
Q.x = A.x * sin(theta/2)
Q.y = A.y * sin(theta/2)
Q.z = A.z * sin(theta/2)
Q.w = cos(theta/2)

Где A - это позиция, а theta - угол, на который вы хотите повернуть камеру (отрегулируйте высоту).

Таким образом, если у вас есть кватернион, вы можете ограничить угол наклона, проверяя каждый раз, что угол поворота плюс / минус фактический угол в порядке.

Я думаю, что вы можете избежать конвертации, если вы установите свои ограничения как

+cos(supLim/2) < (Q.w + P.w) < -cos(infLim/2)

Поскольку косинус является непрерывной функцией, он должен работать.

Если бы вы могли опубликовать код, который вы на самом деле используете, возможно, мы можем помочь немного больше.

...