在 OpenGL 中保持旋转
Posted
技术标签:
【中文标题】在 OpenGL 中保持旋转【英文标题】:Preserving rotations in OpenGL 【发布时间】:2010-10-01 02:34:01 【问题描述】:我正在 OpenGL 中绘制一个对象(例如,一个立方体),用户可以通过在窗口上单击/拖动鼠标来旋转该对象。立方体是这样绘制的:
void CubeDrawingArea::redraw()
Glib::RefPtr gl_drawable = get_gl_drawable();
gl_drawable->gl_begin(get_gl_context());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glRotated(m_angle, m_rotAxis.x, m_rotAxis.y, m_rotAxis.z);
glCallList(m_cubeID);
glPopMatrix();
gl_drawable->swap_buffers();
gl_drawable->gl_end();
并用这个函数旋转:
bool CubeDrawingArea::on_motion_notify_event(GdkEventMotion* motion)
if (!m_leftButtonDown)
return true;
_3V cur_pos;
get_trackball_point((int) motion->x, (int) motion->y, cur_pos);
const double dx = cur_pos.x - m_lastTrackPoint.x;
const double dy = cur_pos.y - m_lastTrackPoint.y;
const double dz = cur_pos.z - m_lastTrackPoint.z;
if (dx || dy || dz)
// Update angle, axis of rotation, and redraw
m_angle = 90.0 * sqrt((dx * dx) + (dy * dy) + (dz * dz));
// Axis of rotation comes from cross product of last / cur vectors
m_rotAxis.x = (m_lastTrackPoint.y * cur_pos.z) - (m_lastTrackPoint.z * cur_pos.y);
m_rotAxis.y = (m_lastTrackPoint.z * cur_pos.x) - (m_lastTrackPoint.x * cur_pos.z);
m_rotAxis.z = (m_lastTrackPoint.x * cur_pos.y) - (m_lastTrackPoint.y * cur_pos.x);
redraw();
return true;
里面有一些 GTK+ 的东西,但它的用途应该很明显。 get_trackball_point() 函数将窗口坐标 X Y 投影到一个半球(虚拟“轨迹球”)上,该半球用作旋转对象的参考点。无论如何,这或多或少是有效的,但是在我完成旋转后,我再次旋转时,立方体会迅速回到原来的位置,很明显,因为下次我旋转时 m_angle 将重置回接近 0。有没有办法避免这种情况并保持轮换?
【问题讨论】:
【参考方案1】:是的,我也遇到过这个问题。
您需要做的是围绕“累积”当前旋转状态的旋转矩阵,并在来自当前拖动操作的旋转矩阵之外使用它。
假设您有两个矩阵,lastRotMx 和 currRotMx。如果你愿意,可以让它们成为 CubeDrawingArea 的成员。
您还没有向我们展示这一点,但我假设只要鼠标按钮按下以进行拖动,m_lastTrackPoint 就会被初始化。发生这种情况时,将 currRotMx 复制到 lastRotMx。
然后在on_motion_notify_event()
中,计算好m_rotAxis和m_angle后,根据m_rotAxis和m_angle新建一个旋转矩阵draggingRotMx;然后通过拖动RotMx乘以lastRotMx并将结果放入currRotMx。
最后,在redraw()
,而不是
glRotated(m_angle, m_rotAxis.x, m_rotAxis.y, m_rotAxis.z);
按 currRotMx 旋转。
更新:或者代替所有这些......我没有测试过这个,但我认为它会工作:
使 cur_pos 成为类成员,以便它保持不变,但它被初始化为零,m_lastTrackPoint 也是如此。 然后,每当开始新的拖动动作时,在初始化 m_lastTrackPoint 之前,让 _3V dpos = cur_pos - m_lastTrackPoint(伪代码)。 最后,当您根据鼠标事件坐标初始化 m_lastTrackPoint 时,从中减去 dpos。
这样,您的 cur_pos 已经从 m_lastTrackPoint 偏移了一个基于过去轨迹球拖动的偏移量累积的量。
错误也可能会累积,但应该是渐进的,以免引起注意。但我想对其进行测试以确保...组合轮换非常棘手,以至于我在没有看到它们的情况下不信任它们。
附:您的用户名令人沮丧。建议再选一个。
附言对于以后来寻找这个问题的答案的人来说,要搜索的关键字是“arcball rotation”。一篇权威文章是 Ken Shoemake 在Graphical Gems IV 中的部分。另请参阅arcball tutorial for JOGL。
【讨论】:
我真的不明白你的第一个建议是如何工作的。旋转角度仍然是当前拖动偏移量的函数,所以在这种情况下,我将使用不同的方法来旋转立方体,但之后,我会再次旋转,角度将接近于零,我又回到了原点。事实上,我确实执行了你的建议并得到了相同的结果。 至于您的第二个建议,我尝试了一些变化,我将当前轨迹球位置作为成员保持,在重绘之前设置 m_lastTrackPoint = 当前轨迹点,并删除对 glPushMatrix( ) 和重绘函数中的 glPopMatrix()。这可以防止它在我下次旋转时被“重置”,但现在在下一次旋转时,它在转换后的坐标系中旋转(我假设)所以我得到奇怪的结果...... @Bit: re: 第一条评论:旋转角度(在 redraw() 中)不仅仅是当前拖动偏移量的函数,而是累积的旋转和当前阻力偏移。开始拖动时是否将 currRotMx 保存到 lastRotMx 中,并在拖动时将 currRotMx 设置为 draggingRotMx 和 lastRotMx 的乘积? @Bit: re: 2nd comment: 是的,我想我也试过你的变种,但没有用。正如你所说,它在转换后的坐标系中旋转,所以它不起作用。如您所知,轮换必须以正确的顺序组成。 @Bit:我的示例代码在huttar.net/lars-kathy/tmp/netwalk3D/netwalk3D.html;查看它加载的 js 文件。轨迹球旋转(累积)与早期版本的 WebGL 一起工作;现在可能行不通。但代码仍应更准确地传达方法。以上是关于在 OpenGL 中保持旋转的主要内容,如果未能解决你的问题,请参考以下文章