max*_*yne 2 c++ opengl 3d drag-and-drop mouse-picking
通过鼠标拾取拖动 3D 点的最佳方法是什么?问题不在于拾取,而在于在 3D 空间中拖动。
我想有两种方法,一种是使用 gluUnProject 获取世界视图坐标并平移 3D 点。这种情况下的问题是,它仅在具有深度值的表面上(使用 glReadPixels),如果鼠标离开表面,它会根据 gluUnProject 的 winZ 组件给出最大或最小深度值。而且在某些情况下它不起作用。
第二种方法是使用 GL_MODELVIEW_MATRIX 沿 XY、XZ、YZ 平面拖动。但这种情况下的问题是,我们如何知道我们是在 XY、XZ 还是 YZ 平面上?我们如何知道轨迹球的前视图在 XY 平面内,如果我们想在侧平面而不是前平面上拖动怎么办?
那么,有没有什么方法可以为我提供准确的 2D 到 3D 坐标,以便我可以轻松地在各个方向上拖动 3D 点,而无需考虑平面情况?一定有办法的,我见过3D软件,他们有完美的拖动功能。
小智 6
我习惯于稍微天真地解决这些用户交互问题(也许不是以数学上最佳的方式),但考虑到它们对性能不是很关键(用户交互部分,不一定是对结果的修改),“足够好”场景)。
对于不受约束的自由拖动对象,您使用 unproject 描述的方法往往效果很好,只需稍加调整即可实现近乎完美的拖动:
...而不是用来尝试glReadPixels提取屏幕深度,您需要在用户拾取和/或选择时获得几何对象/网格的概念。现在只需投影该对象的中心点即可获得屏幕深度。然后,您可以在屏幕 X/Y 中拖动,保持从该投影获得的相同 Z,然后取消投影以获得从先前中心到新中心的最终平移增量以变换对象。这也使得它“感觉”就像您从对象的中心拖动一样,这往往非常直观。
对于自动约束拖动,检测它的快速方法是首先获取“视平面法线”。使用您习惯的投影/非投影函数的一种快速方法(可能会让数学家皱眉)是在屏幕空间中取消投影视口中心的两个点(一个具有近 z 值,一个具有远 z 值)并得到这两点之间的单位向量。现在您可以使用点积找到最接近法线的世界轴。另外两个世界轴定义了我们想要拖动的世界平面。
然后,再次使用这些方便的非投影函数来沿着鼠标光标获取光线就变得很简单了。之后,您可以在拖动光标时重复光线/平面相交,以根据增量计算平移向量。
对于更灵活的约束,小控件(又名操纵器,基本上是 3D 小部件)可以派上用场,以便用户可以根据他想要的小控件的哪些部分来指示他想要哪种类型的拖动约束(平面、轴、无约束等)。选取/拖动。对于轴约束,射线/直线或直线/直线相交很方便。
按照评论中的要求,从视口检索光线(C++ 伪代码):
// Get a ray from the current cursor position (screen_x and screen_y).
const float near = 0.0f;
const float far = 1.0f;
Vec3 ray_org = unproject(Vec3(screen_x, screen_y, near));
Vec3 ray_dir = unproject(Vec3(screen_x, screen_y, far));
ray_dir -= ray_org;
// Normalize ray_dir (rsqrt should handle zero cases to avoid divide by zero).
const float rlen = rsqrt(ray_dir[0]*ray_dir[0] +
ray_dir[1]*ray_dir[1] +
ray_dir[2]*ray_dir[2]);
ray_dir[0] *= rlen;
ray_dir[1] *= rlen;
ray_dir[2] *= rlen;
Run Code Online (Sandbox Code Playgroud)
然后,我们与从鼠标光标获得的射线进行射线/平面相交,以找出当用户开始拖动时射线与平面相交的位置(相交将为我们提供一个 3D 点)。之后,它只是根据用户拖动鼠标时重复执行此操作收集的点之间的增量来平移对象。对象在沿着平面约束移动时应该直观地跟随鼠标。
轴拖动基本上是相同的想法,但是我们将射线变成一条线并进行线/线相交(鼠标线相对于轴约束的线,给我们一个最近的点,因为线通常不会完美相交),返回一个 3D 点,我们可以从该点使用增量沿约束轴平移对象。
请注意,轴/平面拖动约束涉及一些棘手的边缘情况。例如,如果一个平面垂直于观察平面(或接近),它可以将物体射向无穷远。沿着垂直线拖动轴也存在同样的情况,就像尝试从前视口(X/Y 观察平面)沿着 Z 轴拖动一样。因此,值得检测线/平面垂直(或接近)的情况并防止在这种情况下拖动,但这可以在您掌握基本概念后完成。
另一个值得注意的技巧是隐藏鼠标光标,以改善某些情况下的“感觉”。例如,由于轴约束,鼠标光标最终可能会变得距离轴本身很远,并且可能看起来/感觉很奇怪。因此,我看到许多商业软件包在这种情况下只是隐藏鼠标光标,以避免暴露鼠标和小玩意/手柄之间的差异,结果往往感觉更自然。当用户释放鼠标按钮时,鼠标光标移动到手柄的视觉中心。请注意,您不应该对平板电脑执行这种隐藏光标拖动操作(它们有点例外)。
这种拾取/拖动/交叉的东西可能非常难以调试,因此值得逐步解决。为自己设定小目标,例如只需在视口中的某处单击鼠标按钮即可创建光线。然后您可以绕轨道运行并确保在正确的位置创建光线。接下来,您可以尝试一个简单的测试,看看该光线是否与世界中的平面(例如 X/Y)相交,并创建/可视化光线和平面之间的交点,并确保其正确。慢慢地、耐心地、一步一步地、调整自己的节奏,你就会取得平稳、自信的进步。尝试一次做太多事情,你可能会在试图找出哪里出了问题时取得非常令人沮丧的不和谐进展。
| 归档时间: |
|
| 查看次数: |
5156 次 |
| 最近记录: |