Quaternion的计算向量工作,从Vector计算Quaternion没有

时间:2013-02-18 15:25:58

标签: c++ math 3d rotation quaternions

所以我使用四元数在3D空间中创建两个点的一段,然后尝试重新计算一个类似的四元数(一个通过空间表示相同的向量;我知道段的自身周围的旋转是未定义的) 。我正在创建这样的片段:

sf::Vector3<float> Start(0, 0, 0);
sf::Vector3<float> End = Start;

//Create a vector from the start to the end
sf::Vector3<float> Translation = Orientation.MultVect(sf::Vector3<float>(0, 1, 0));

//Add that vector onto the start position
End.x += Translation.x * Length;
End.y += Translation.y * Length;
End.z += Translation.z * Length;

Orientation :: MultVect()的位置如下:

sf::Vector3<float> Quaternion::MultVect(sf::Vector3<float> Vector)
{
    //From http://www.idevgames.com/articles/quaternions
    Quaternion VectorQuat = Quaternion();
    VectorQuat.x = Vector.x;
    VectorQuat.y = Vector.y;
    VectorQuat.z = Vector.z;
    VectorQuat.w = 0.0;

    Quaternion Inverse = (*this);
    Inverse.Invert();

    Quaternion Result = Inverse * VectorQuat * (*this);

    sf::Vector3<float> ResultVector;
    ResultVector.x = Result.x;
    ResultVector.y = Result.y;
    ResultVector.z = Result.z;

    return ResultVector;
}

现在这个功能似乎在其他环境中运行得相当好,所以我认为问题不在这里,但你永远不会知道。我还应该提一下,这个点最终会出现在我预期的位置,因为Quaternion I feed if(我从Euler角度构造,有时与其他四元数相乘)。

对我来说,问题在于从StartEnd重新计算四元数。为此,我使用此函数,在将场景中的对象定向到其他对象时效果很好(除非所讨论的对象沿着完全相同的Y轴,在这种情况下,我得到具有NaN值的四元数)。我是这样做的:

Quaternion Quaternion::FromLookVector(sf::Vector3<float> FromPoint, sf::Vector3<float> ToPoint)
{
    ///Based on this post:
    ///http://stackoverflow.com/questions/13014973/quaternion-rotate-to
    //Get the normalized vector from origin position to ToPoint
    sf::Vector3<double> VectorTo(ToPoint.x - FromPoint.x,
                                 ToPoint.y - FromPoint.y,
                                 ToPoint.z - FromPoint.z);
    //Get the length of VectorTo
    double VectorLength = sqrt(VectorTo.x*VectorTo.x +
                               VectorTo.y*VectorTo.y +
                               VectorTo.z*VectorTo.z);
    //Normalize VectorTo
    VectorTo.x /= -VectorLength;
    VectorTo.y /= -VectorLength;
    VectorTo.z /= -VectorLength;

    //Define a unit up vector
    sf::Vector3<double> VectorUp(0, -1, 0);

    //The X axis is the cross product of both
    //Get the cross product as the axis of rotation
    sf::Vector3<double> AxisX(VectorTo.y*VectorUp.z - VectorTo.z*VectorUp.y,
                              VectorTo.z*VectorUp.x - VectorTo.x*VectorUp.z,
                              VectorTo.x*VectorUp.y - VectorTo.y*VectorUp.x);
    //Normalize the axis
    //Get the length of VectorTo
    double AxisXLength = sqrt(AxisX.x*AxisX.x +
                              AxisX.y*AxisX.y +
                              AxisX.z*AxisX.z);

    //Normalize VectorTo
    AxisX.x /= AxisXLength;
    AxisX.y /= AxisXLength;
    AxisX.z /= AxisXLength;

    //Get the adjusted Y vector
    //Get the cross product of the other two axes
    sf::Vector3<double> AxisY(VectorTo.y*AxisX.z - VectorTo.z*AxisX.y,
                              VectorTo.z*AxisX.x - VectorTo.x*AxisX.z,
                              VectorTo.x*AxisX.y - VectorTo.y*AxisX.x);
    //Normalize the axis
    //Get the length of VectorTo
    double AxisYLength = sqrt(AxisY.x*AxisY.x +
                              AxisY.y*AxisY.y +
                              AxisY.z*AxisY.z);
    //Normalize VectorTo
    AxisY.x /= AxisYLength;
    AxisY.y /= AxisYLength;
    AxisY.z /= AxisYLength;

    //A matrix representing the Thing's orientation
    GLfloat RotationMatrix[16] = {(float)AxisX.x,
                                  (float)AxisX.y,
                                  (float)AxisX.z,
                                  0,
                                  (float)AxisY.x,
                                  (float)AxisY.y,
                                  (float)AxisY.z,
                                  0,
                                  (float)VectorTo.x,
                                  (float)VectorTo.y,
                                  (float)VectorTo.z,
                                  0,
                                  0,
                                  0,
                                  0,
                                  1};

    Quaternion LookQuat = Quaternion::FromMatrix(RotationMatrix);

    //Reset the quaternion orientation
    return LookQuat;
}

因此,当我计算片段时,我还会检查它们的重构值是什么,如下所示:

sf::Vector3<float> Start(0, 0, 0);
sf::Vector3<float> End = Start;

//Create a vector from the start to the end
sf::Vector3<float> Translation = Orientation.MultVect(sf::Vector3<float>(0, 1, 0));
//Add that vector onto the start position
End.x += Translation.x * Length;
End.y += Translation.y * Length;
End.z += Translation.z * Length;

std::cout << "STATIC END     (";
std::cout << End.x << ",";
std::cout << End.y << ",";
std::cout << End.z << ")\n";

///TEST
Quaternion Reconstructed = Quaternion::FromLookVector(Start, End);
Translation = Reconstructed.MultVect(sf::Vector3<float>(0, 1, 0));
sf::Vector3<float> TestEnd = Start;
TestEnd.x += Translation.x * Length;
TestEnd.y += Translation.y * Length;
TestEnd.z += Translation.z * Length;

std::cout << "RECONSTRUCTED END (";
std::cout << TestEnd.x << ",";
std::cout << TestEnd.y << ",";
std::cout << TestEnd.z << ")\n";

两者不匹配。例如,如果静态终点为(0,14.3998,0.0558498),则重新计算的点为(0,8.05585,-6.39976)。但两者应该是相同的。旋转的未定义部分不应该改变终点的位置,只能改变滚动(或Z旋转,或者你想要调用的任何部分),因为这是一个段,这并不重要。

请注意,当我最终将其用于除简单段之外的其他内容时,roll 重要,这就是为什么我使用向上向量来确保我沿着这些段放置的对象将始终使它们的顶部尽可能朝上(如果需要,可以直接向上或向下看的物体可以单独确定特殊的任意滚动)。另一个目标是创建多个串在一起的段,每个段相对于前一个段的方向旋转,而不是相对于全局空间旋转。

那我在这里做错了什么?为什么我不能重新计算与第一个执行相同翻译的第二个四元数?

1 个答案:

答案 0 :(得分:2)

我不完全确定你是如何计算两个向量之间的'旋转'四元数,但我很确定它非常麻烦。至少,如果我理解正确的话,你会“看”向某个方向指向的向量,并且对象从原点(0,0,0)沿着那个方向“看起来”,对吗?

如果是上述情况,那应该不会太困难。我发现一件非常奇怪的事情是你的四元数 - 向量乘法似乎是相反的顺序。我将四元数*向量定义为:

quat qt = *this * quat(0, vec.x, vec.y, vec.z) * inverse();
return vec3(qt.x, qt.y, qt.z);

其中quat构造函数定义为quat(w,x,y,z),而inverse()方法返回副本。逆等于共轭,并且定义为(w,-x,-y,-z)。但是,为了实现这一点,你的四元数必须被归一化,只有这样它们才会真正代表一个方向(只有这样才能使逆相等于共轭)。然后我将四元数乘法定义如下:

// This describes A * B (not communative!)
w = A.w * B.w - A.x * B.x - A.y * B.y - A.z * B.z;
x = A.w * B.x + A.x * B.w + A.y * B.z - A.z * B.y;
y = A.w * B.y + A.y * B.w + A.z * B.x - A.x * B.z;
z = A.w * B.z + A.z * B.w + A.x * B.y - A.y * B.x;

除此之外,您希望能够从'angle-axis'构造四元数。这意味着它应该采用旋转轴和围绕该轴旋转的角度(以弧度表示)。我只会给你那个算法,因为它没有直观的意义:

// if axis is already unit length, remove the division
double halfAngle = angle * 0.5f; // In radians
double scale = sin(halfAngle) / axis.magnitude();

w = cos(halfAngle);
x = axis.x * scale;
y = axis.y * scale;
z = axis.z * scale;

所以现在我们只需要计算一个要旋转的轴,以及我们想要围绕它旋转多少,以弧度为单位。乍一看,这似乎很复杂,但这只是了解正在发生的事情。你有两个向量,A和B.你想要计算一个描述旋转FROM A,TO B的四元数。为了让轴旋转,我们只想要两个垂直轴,显然这可以通过采取交叉产品。如果你使用的是右手坐标系,那就是:

axis = A × B

如果你正在使用左手坐标系,我认为你应该反转顺序,但不要接受我的话。现在来获取两个向量之间的角度。这可以通过采用点积来非常简单地完成。唯一的问题是你必须对两个向量进行标准化,因此它们的长度为1,并且不会改变点积的结果。这样点积将返回角度的余弦,因此要获得我们可以做的实际角度:

angle = acos(normalize(A) * normalize(B))

乘法符号代表点积。现在我们只是在上面给出的算法中插入轴和角度,并且我们有一个四元数描述从外观向量A到看向量B的'旋转'。现在如果向量指向完全相同的方向,那将是不明智的应用算法,因为轴将是(0,0,0)。如果你看一下算法,我希望你看到它会尝试除以零或只是输出全零。因此,每当我应用该算法时,我首先检查轴是否都不是全零。

您目前使用的公式对我来说似乎非常奇怪和低效。我真的不明白为什么你第一次计算矩阵,从矩阵计算四元数是一个非常昂贵的计算。事实上,我相信计算相反的,四元数的矩阵,甚至更快。

无论如何,祝你好运!