用鼠标抓取拖动三维点的最佳方法是什么。问题不是采摘,而是在3D空间中的拖动。
我认为有两种方法,一种是使用gluUnProject将视图转到世界坐标,并翻译3D点。这种情况下的问题是,它只存在于具有深度值的曲面上(使用glReadPixels),如果鼠标离开表面,则根据gluUnProject的winZ组件给出最大或最小深度值。但在某些情况下不起作用。
第二种方法是使用GL_MODELVIEW_MATRIX沿XY、XZ、YZ平面拖曳。但是这个例子的问题是,我们怎么知道我们在XY,XZ或者YZ飞机上?我们怎么知道轨迹球的正面是在XY平面上,如果我们想要拖到侧面而不是前面的平面上呢?
那么,有没有办法给我精确的二维到三维坐标,这样我就可以轻松地把三维点拖向各个方向,而不考虑平面的情况?一定有一些方法,我看过3D软件,他们有完美的拖动功能。
发布于 2015-05-28 18:28:24
我习惯于有些天真地解决这些用户交互问题(也许不是以数学上最优的方式),但“足够好”考虑到它们不是非常关键的性能(用户交互部分,不一定是对场景的修改)。
对于对象的不受约束的自由拖动,使用unproject描述的方法很好地工作,通常会提供接近像素的完美拖动,只需稍作调整:
..。您不需要使用glReadPixels
来提取屏幕深度,而是希望在用户选择和/或选择时使用几何对象/网格的概念。现在,只需投影该对象的中心点,以获得屏幕深度。然后,您可以在屏幕X/Y中拖动,保持从这个投影得到的相同的Z,并取消项目以获得从前一个中心到新中心的转换增量来转换对象。这也使它“感觉”,就像你从物体的中心拖动,这往往是相当直观的。
对于自动约束的拖动,一种快速的检测方法是首先抓取一个“视面正常”。一种快速的方法(可能会使数学家皱眉)使用那些你习惯的投影/反投影函数,就是在屏幕空间中取消在视口中心的两个点(一个是近z值,另一个是远z值),在这两个点之间得到一个单位向量。现在你可以用点积找到最接近正常轴的世界轴。另外两个世界轴定义了我们想要拖着的世界飞机。
然后,简单地使用这些方便的非投影函数来获得鼠标光标的光线。在此之后,您可以执行重复的射线/平面交叉,因为您正在拖动游标,以计算一个从三角洲的翻译矢量。
对于更灵活的约束,gizmo (也称为机械手,基本上是3D部件)可以派上用场,这样用户就可以根据他选择/拖动的gizmo的哪个部分来指示他想要什么样的拖动约束(平面、轴、无约束等等)。对于轴约束,射线/线或线/线相交是方便的。
按照注释中的要求,从视口检索光线(C++-ish伪代码):
// 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;
然后,我们用鼠标光标得到的光线做一个射线/平面交点,以确定当用户开始拖动时,光线在哪里与平面相交(交点将给我们一个3D点)。在此之后,它只是在用户拖动鼠标时,在重复执行过程中收集到的点之间通过增量来转换对象。物体在沿平面约束移动时,应直观地跟随鼠标。
轴拖动基本上是相同的想法,但是我们把光线变成一条线,并做一条线/线的交点(鼠标对轴约束的线,给我们一个最近的点,因为这些线通常不会完全相交),给我们一个3D点,我们可以利用这个三角形沿着约束轴来转换对象。
请注意,有一些棘手的边缘情况涉及轴/平面拖动约束。例如,如果一个平面垂直于观察平面(或接近),它可以将物体发射到无穷远。同样的情况下,轴沿垂直线拖动,就像试图从前视图(X/Y视图平面)沿Z轴拖动一样。因此,在这种情况下,检测直线/平面垂直(或接近)并防止拖曳是值得的,但这可以在基本概念生效之后完成。
另一个值得注意的技巧是隐藏鼠标光标,以改善某些情况下的“感觉”。例如,有了axis约束,鼠标光标可能会变得离轴本身很远,而且看起来/感觉很奇怪。因此,我已经看到许多商业包只是隐藏鼠标光标在本例中,以避免显示鼠标和gizmo/句柄之间的差异,因此它会感觉更自然一些。当用户释放鼠标按钮时,鼠标光标被移动到手柄的视觉中心。请注意,您不应该对平板电脑执行隐藏游标拖动操作(它们是一个例外)。
这种拾取/拖放/交叉的东西很难调试,所以它值得在婴儿步骤中解决。为自己设定一个小目标,就像点击某个视口中的鼠标按钮来创建光线一样。然后你可以绕着它转,确保射线在正确的位置上产生。接下来,您可以尝试一个简单的测试,看看该射线是否与世界上的一个平面(例如X/Y)平面相交,并创建/可视化射线与平面之间的交点,并确保这是正确的。采取小的,耐心的婴儿步,自己的步调,你会有顺利,自信的进展。试着一次做太多事情,你可能会有非常令人沮丧的、令人不快的进展,试图找出你的错误所在。
发布于 2015-05-28 20:24:09
这是一个非常有趣的图集。与您描述的颜色标记方法一样,z缓冲区也是一种方法。在这里,对同一主题也进行了类似的讨论:
Generic picking solution for 3D scenes with vertex-shader-based geometry deformation applied
OpenGL - Picking (fastest way) How to get object coordinates from screen coordinates?
这也类似于已经讨论过的对象选择,包括它们的优缺点:http://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/picking-with-an-opengl-hack/
实际上,我的答案是,没有唯一的最佳方法来做到这一点。正如你还提到的,他们有优点也有缺点。我个人喜欢暴饮暴食,因为它很容易,其结果是好的。此外,您不能使用屏幕空间在任何方向移动顶点,因为屏幕空间是2D的,并且没有唯一的方法将其映射回三维几何空间。希望它有帮助:)。
https://stackoverflow.com/questions/30519214
复制相似问题