使用选择框选择3D曲面(以两种不同的方式)

时间:2015-08-21 16:52:26

标签: opengl 3d mouse-picking

我正在创建一个建模软件。我的模型都是由平面多边形组成的,这些多边形只是我用OpenGL显示的一组有序顶点。我已经做了很多搜索,令我惊讶的是,找不到我正在寻找的应用程序的大量信息。

我正在尝试使用矩形框来选择曲面。这听起来很简单,但我希望它的工作方式与此方法在许多程序中的工作方式相同。这些是我要求的要求:

  1. 我想要一个从左边开始的矩形,然后右边只选择那些完全包含在框内的对象。
  2. 从右侧开始向左的矩形应选择任何被触摸的表面(不必完全封闭。
  3. 应选择/触摸矩形中的所有对象。换句话说,我想选择对象是否可见。即使被另一个表面覆盖,也应该选择适合盒子内部的所有东西。
  4. 列表中的第3位是最重要的。选择1和2都是首选,但如果它被证明过于难以实现,我只能使用其中一个。

    我已经查看了有关3D拾取的各种其他帖子,似乎大多数建议采用颜色或光线投射。我使用颜色选择进行正常的点击选择,但是因为我希望选择框选择不可见的表面,所以这不是一个选项。似乎光线投射仅适用于单个点而不是盒子。那么还有其他方法可以直接实现我的目标吗?我认为这将是一项相当普遍的任务,因为它似乎存在于许多建模软件中,但遗憾的是我无法找到适合我需要的方法。

    算法的伪代码将被理解,但不是必需的。至少我正在寻找一种方法,我可以自己研究并找到一些例子;我根本不知道适当的地方。

3 个答案:

答案 0 :(得分:6)

在CPU上执行自己的交集计算当然是一种选择。但根据我对你的要求的理解,我认为你也可以让OpenGL完成工作,这应该更容易,更有效率。

方法概述

想到的机制是遮挡查询。它们允许您计算已渲染的像素。如果将此与剪刀测试结合使用,则可以计算在选择矩形内渲染的像素。

用例申请

用例2对于这种方法来说更简单。将选择矩形设置为剪刀矩形,并为每个曲面渲染遮挡查询。然后检查查询的结果,查询结果大于0的所有表面都在选择矩形内有像素。

用例1有点棘手。要知道表面是否完全包含在矩形内,您需要两次通过。如上所述,您可以使用遮挡查询进行渲染,并启用剪刀测试。然后你再次做同样的事情,剪刀测试被禁用。如果曲面对两个传递具有相同的查询结果,则它完全位于矩形内。

实施

我不打算为此提供完整的代码。它应该都非常简单。但这里有一些指针和代码片段。调用显示为C绑定。我希望它与Python绑定的相同之处是显而易见的。

首先,由于您希望在选择中包含隐藏曲面,因此需要禁用深度测试:

glDisable(GL_DEPTH_TEST);

由于您并不真的需要产生输出,并且可能不想打扰视觉渲染输出,您可能还想禁用颜色输出:

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

如果您启用了背面剔除,您可能也想要禁用它:

glDisable(GL_CULL_FACE);

然后,对于上面提到的通道,你只想计算选择矩形内的像素,设置剪刀矩形,并启用剪刀测试:

glScissor(selectionLeft, selectionBottom, selectionWidth, selectionHeight);
glEnable(GL_SCISSOR_TEST);

对于使用遮挡查询进行渲染,您需要为每个曲面创建一个查询对象:

GLuint queryIds[surfaceCount];
glGenQueries(surfaceCount, queryIds);

然后对于每个曲面,使用k作为循环索引:

glBeginQuery(GL_SAMPLES_PASSED, queryIds[k]);
// render surface k
glEndQuery(GL_SAMPLES_PASSED);

渲染完所有曲面后,您可以获得查询结果:

GLint pixelCounts[surfaceCount];
// for all surfaces k
glGetQueryObjectiv(queryIds[k], GL_QUERY_RESULT, &pixelCounts[k]);

然后评估像素数,以决定应如上一节中针对每个用例所述选择哪些表面。

在您完成准备好再次渲染之后,不要忘记重置所有状态。深度测试,彩色面罩,剪刀测试等。

答案 1 :(得分:5)

我可以告诉你,颜色挑选不起作用;你必须为每个对象做一次传递以满足要求(3),因为只有一个像素进入帧缓冲区。

对于光线投射,它确实只测试单个点,但您实际上可以通过取消选择选择矩形的四个角来创建测试体积。您可以在近平面(Win_Z = 0)和远平面(Win_Z = 1)的世界空间中找到坐标,并使用此坐标从2D选区域构建3D体积。

这样做的体积称为平截头体(假设透视投影),它看起来像顶部被切掉的金字塔。 Frustum交叉测试有很好的文档记录,任何讨论“视锥体剔除”的内容都应该为您提供足够的背景来实现它。如果您可以使用轴对齐的边界框和/或球体来近似这些对象的边界,那么您的生活将会轻松得多。

following diagram很好地说明了这个测试卷:

enter image description here

答案 2 :(得分:0)

我很快就知道了,但是实际上颜色选择可以通过以下方法在选择深层对象时起作用:通过按Shift +鼠标滚动进行选择剥离。

或者换句话说,当您进行第一个矩形测试时,请记住矩形位置,然后让用户按shift +鼠标滚动,当用户滚动时,从渲染中暂时删除已选择的对象(因此会剥皮)(仅在渲染到选择缓冲区时)并添加任何新的选定对象到选定数组。继续,直到用户停止滚动或按Shift键为止。

注意事项是,一旦您移动或旋转摄像机,此方法将无法很好地工作,但是当用户这样做时,您可以使最后一个矩形无效或在用户进行平移时将摄像机的三角矩阵临时应用于整个世界,而在用户没有移动时将鼠标滚动到整个世界使上一次选择无效,但由于这个原因,除非您坚持使用较旧的旧硬件或移动设备(opengl es 2没有扩展名),否则它最好还是坚持遮挡查询

另一个是对于小于1像素的对象,颜色拾取失败,但这很少出现(尽管对于矩形拾取,根据用例选择“不可见”对象可能是有意义的)