Кватернионы, поворот модели и выравнивание по направлению

Предположим, у вас есть кватернион, описывающий вращение 3D-модели.

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

Для космического корабля я хочу, чтобы кабина указывала на цель.

Вот некоторый код, который у меня есть... Он не делает то, что я хочу, и я не знаю, почему...

        if (_target._ray.Position != _obj._ray.Position)
        {
            Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
            float angle = (float)Math.Acos(Vector3.Dot(vec, _obj._ray.Direction));
            Vector3 cross = Vector3.Cross(vec, _obj._ray.Direction);

            if (cross == Vector3.Zero)
                cross = _obj._side;

            _obj._rotationQuaternion *= Quaternion.CreateFromAxisAngle(cross,angle);
        }
        // Updates direction, up, side vectors and model Matrix
        _obj.UpdateMatrix();

через некоторое время кватернион вращения заполняется почти нулем в точках X, Y, Z и W

Любая помощь? Спасибо ;-)


person Henrique Rocha    schedule 06.11.2012    source источник


Ответы (3)


Ваш код немного странный.

if (_target._ray.Position != _obj._ray.Position)
{

Это может быть или не быть правильным. Очевидно, вы переопределили компаратор равенства. Здесь правильно было бы сделать так, чтобы скалярное произведение между двумя лучами (единичной длины) было близко к 1. Если лучи имеют одно и то же происхождение, то, предположительно, имеют равные «позиции», что означает, что они такой же.

  Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);

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

Вот псевдокод того, что я рекомендую:

normalize3(targetRay);
normalize3(objectRay);
angleDif = acos(dotProduct(targetRay,objectRay));
if (angleDif!=0) {
  orthoRay = crossProduct(objectRay,targetRay);
  normalize3(orthoRay);
  deltaQ = quaternionFromAxisAngle(orthoRay,angleDif);
  rotationQuaternion = deltaQ*rotationQuaternion;
  normalize4(rotationQuaternion);
}

Здесь следует отметить две вещи:

  1. Кватернионы не коммутативны. Я предположил, что ваши кватернионы представляют собой вращающиеся векторы-столбцы; поэтому я поставил deltaQ слева. Непонятно, что делает ваш оператор *=.
  2. Важно регулярно нормализовать ваши кватернионы после умножения. В противном случае накапливаются небольшие ошибки, и они уходят от единичной длины, вызывая всевозможные огорчения.
person JCooper    schedule 07.11.2012

Это ярлык, который я использовал, чтобы получить кватернион для поворота фиксация цели:

Matrix rot = Matrix.CreateLookAt(_arrow.Position, _cube.Position, Vector3.Down);
_arrow.Rotation = Quaternion.CreateFromRotationMatrix(rot);

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

введите здесь описание изображения

Как только вы получите этот кватернион (от космического корабля к цели), вы можете использовать Quaternion.Lerp() для интерполяции между текущим вращением корабля и выровненным. Это придаст вашему вращению плавный переход (а не просто привязку к цели).


Кстати, может случиться так, что ваше вращение будет уменьшено до нуля, потому что вы используете *= при назначении ему.

person neeKo    schedule 07.11.2012

МОЙ БОГ! Это сработало!!!

            Vector3 targetRay = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
        Vector3 objectRay = Vector3.Normalize(_obj._ray.Direction);
        float angle = (float)Math.Acos(Vector3.Dot(targetRay, objectRay));

        if (angle!=0)
        {
            Vector3 ortho = Vector3.Normalize(Vector3.Cross(objectRay, targetRay));
            _obj._rotationQuaternion = Quaternion.CreateFromAxisAngle(ortho, angle) * _obj._rotationQuaternion;
            _obj._rotationQuaternion.Normalize();
        }
        _obj.UpdateMatrix();

Спасибо большое JCooper!!!

И niko мне нравится идея с Lerp ;-)

person Henrique Rocha    schedule 07.11.2012