我正在使用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);
当我运行此代码时,当我在窗口周围移动鼠标时,立方体会旋转,但不会像我期望的那样。此外,立方体似乎在某些点突然“翻转”它的方向。
有人可以指出我应该如何正确实施这个方法吗?
谢谢!
答案 0 :(得分:1)
您提到的不连续性,即“翻转”,来自您在lookat
矩阵构造中使用固定的“向上”方向。如果“眼睛”方向是“向上”方向,则lookat
矩阵表现奇怪。因此,您的代码需要适当地调整“向上”。
但它的基本问题是没有旋转你希望它旋转的方式?这来自于使用错误的工具来解决问题,这也来自于缺乏对问题本身的完全理解。
我将假设您想在Maya或Blender3D等3D建模应用程序中实现类似鼠标旋转控件的功能。如果你注意,你会发现这些鼠标控件实际上考虑了查看器的方向。如果更改正在查看对象的角度,则应用于该对象的旋转会更改。
或者换句话说,您想要控制对象相对于相机的方向。但是你想要最终设置的矩阵是相对于 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);
}
函数的输入是一个四元数,表示旋转 delta ,根据鼠标在帧之间移动的程度计算。此函数的作用是将该delta应用于对象的当前方向。但是,增量是在相机空间(这是用户看到的空间)。由于存储对象的方向位于世界空间中,因此我们需要在将delta应用到对象的方向之前将其转换为世界空间。
这是invViewQuat * rot * viewQuat
的工作。这个数学运作的原因是a bit complex进入这里。