我在世界空间中有一些物体,让我们说在(0,0,0)并且想要将它旋转到面(10,10,10)。
我如何使用四元数?
答案 0 :(得分:8)
这个问题没有多大意义。你说你希望一个对象“面对”一个特定的点,但这并不能提供足够的信息。
首先,面对这个方向意味着什么?在OpenGL中,这意味着本地参考帧中的 -z 轴与某些外部参照系中的指定方向对齐。为了实现这种对齐,我们需要知道对象的相关轴当前“面向”的方向。
但是,仍然没有定义唯一的转换。即使您知道使 -z 轴点成为什么方向,该对象仍然可以自由旋转围绕该轴。这就是函数gluLookAt()
要求您提供'at'方向和'up'方向的原因。
我们需要知道的下一件事是最终结果需要采用什么格式?对象的方向通常以四元数格式存储。但是,如果要以图形方式旋转对象,则可能需要旋转矩阵。
所以让我们做一些假设。我假设您的对象位于世界点 c 的中心,并且具有默认对齐方式。即,对象的 x , y 和 z 轴与世界的 x , y对齐和 z 轴。这意味着对象相对于世界的方向可以表示为单位矩阵或标识四元数:[1 0 0 0]
(使用四元数约定,其中 w 首先出现)。
如果您想要最短的旋转将对象的 -z 轴与点 p := [px py pz]对齐,那么您将围绕轴旋转φ的一个即可。现在我们将找到这些价值观。首先,我们通过对矢量 pc 进行归一化,然后使用单位长度 -z 向量的交叉积来找到轴 a ,然后再次归一化:
a = normalize(crossProduct( -z ,规范化( p-c )));
通过取其点积的反余弦得到的这两个单位向量之间的最短角度:
φ= acos(dotProduct( -z ,规范化( p-c )));
不幸的是,这是两个矢量形成的角度的绝对值的量度。当围绕 a 旋转时,我们需要弄清楚它是正还是负。必须有一种更优雅的方式,但想到的第一种方法是找到第三个轴,垂直于 a 和 -z ,然后从中取出符号它与我们的目标轴的点积。可见:b = crossProduct( a , -z );
if(dotProduct( b ,normalize( p-c ))< 0)φ=-φ;
一旦我们获得了轴和角度,将其转换为四元数很容易:
q = [cos(φ/ 2)sin(φ/ 2) a ];
这个新的四元数表示对象的新方向。它可以转换为矩阵用于渲染目的,或者如果需要,可以使用它直接旋转对象的顶点,使用四元数乘法规则。
答案 1 :(得分:1)
可以在Ogre::Vector3 class的OGRE源代码中找到计算表示两个向量之间旋转的四元数的示例。
答案 2 :(得分:1)
为了回应你的澄清并回答这个问题,我无耻地复制了一个非常有趣和简洁的算法,用于找到两个向量之间的quat,看起来像我以前从未见过here。在数学上,它似乎是有效的,因为你的问题是关于它背后的数学,我相信你将能够将这个伪代码转换为C ++。
quaternion q;
vector3 c = cross(v1,v2);
q.v = c;
if ( vectors are known to be unit length ) {
q.w = 1 + dot(v1,v2);
} else {
q.w = sqrt(v1.length_squared() * v2.length_squared()) + dot(v1,v2);
}
q.normalize();
return q;
如果您需要帮助澄清该伪代码的任何位,请告诉我。应该是直截了当的。
dot(a,b) = a1*b1 + a2*b2 + ... + an*bn
和
cross(a,b) = well, the cross product. it's annoying to type out and
can be found anywhere.
答案 3 :(得分:0)
您可能想要使用SLERP(球面线性插值)。请参阅此article以获取有关如何在c ++中执行此操作的参考