我正在使用OpenGL和GLUT来显示多维数据集。现在,我希望能够通过在窗口上拖动鼠标来围绕立方体旋转摄像机。当鼠标水平移动时,多维数据集应绕其垂直轴旋转,当鼠标垂直移动时,多维数据集应绕其水平轴旋转,二者均以固定距离d
进行。
[为了实现这一点,我尝试用球坐标theta
和phi
表示相机。当鼠标在屏幕上水平移动时,我增大(右)或减小(左)theta
,而当鼠标垂直移动时,我增大(上)或减小(下)phi
。然后,我使用以下方程式确定相机和相关视图矩阵的笛卡尔位置:
float eye_x = d * sin(phi) * cos(theta);
float eye_y = d * sin(phi) * sin(theta);
float eye_z = d * cos(phi);
glm::vec3 centre(0.0f, 0.0f, 0.0f);
glm::vec3 up(0, 1, 0);
view_matrix = glm::lookAt(eye, centre, up);
当我运行此代码时,当我在窗口上移动鼠标时,立方体会旋转,但不会以我期望的方式旋转。另外,立方体似乎在某些点突然“翻转”其方向。
有人可以为我指出正确的方向,我应该如何正确实施此方法?
您提到的不连续性,即“翻转”,是由于在lookat
矩阵构造中使用了固定的“向上”方向而产生的。如果“眼睛”方向为“向上”方向,则lookat
矩阵的行为会异常。因此,您的代码将需要直接调整“向上”。
但是,基本问题是它没有按照您希望的方式旋转?这是因为使用了错误的工具来解决问题,也由于缺乏对问题本身的全面了解。
我假设您要在Maya或Blender3D等3D建模应用程序中实现类似鼠标旋转控件的功能。如果您注意的话,您会注意到viewer的方向实际上是由此类鼠标控件考虑在内的。如果您更改查看对象的角度,则应用于该对象的旋转将发生变化。
或者换句话说,您想要控制对象相对于相机的方向。但是,您最终要设置的矩阵是相对于world。
的旋转。所以这就是你要做的。您需要根据鼠标移动生成方向偏移。这是相对于相机而言的。然后,您需要将该方向偏移转换为相对于世界的偏移。然后,将其应用于对象的当前方向。
球面坐标不能执行此操作。它们以相对于世界的角度进行交易。并且即使不是不可能,变换这样的角度也是困难的。相反,您需要使用方向[[直接,而不是角度。
这意味着矩阵或四元数。我编写的非官方GL SDK的class that does this。关键代码是:
void ObjectPole::RotateViewDegrees( const glm::fquat &rot, bool bFromInitial )
{
if(!m_bIsDragging)
bFromInitial = false;
if(m_pView)
{
glm::fquat viewQuat = glm::quat_cast(m_pView->CalcMatrix());
glm::fquat invViewQuat = glm::conjugate(viewQuat);
m_po.orientation = glm::normalize((invViewQuat * rot * viewQuat) *
(bFromInitial ? m_startDragOrient : m_po.orientation));
}
else
RotateWorldDegrees(rot, bFromInitial);
}
函数的输入是表示旋转度的四元数,其基于鼠标在帧之间移动了多少而计算出。该函数的工作是将该增量应用于对象的当前方向。但是,增量位于相机空间(用户正在查看的空间)中。由于存储的对象的方向位于世界空间中,因此需要在将增量应用于对象的方向之前将其转换为世界空间。
这是invViewQuat * rot * viewQuat
的工作。数学起作用的原因是a bit complex。