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

ssc*_*lli 8 opengl 3d mouse-picking

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

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

  1. 我想要一个从左边开始的矩形,然后右边只选择那些完全包含在框内的对象.
  2. 从右侧开始向左的矩形应选择任何被触摸的表面(它不必完全封闭.
  3. 应选择/触摸矩形中的所有对象.换句话说,我想选择对象是否可见.即使被另一个表面覆盖,也应该选择适合盒子内部的所有东西.

名单上的第3位是最重要的.选择1和2都是首选,但如果它被证明过于难以实现,我只能使用其中一个.

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

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

Ret*_*adi 6

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

方法概述

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

用例申请

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

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

履行

我不会为此提供完整的代码.它应该都非常简单.但这里有一些指针和代码片段.调用显示为C绑定.我希望很明显,Python绑定的外观如何.

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

glDisable(GL_DEPTH_TEST);
Run Code Online (Sandbox Code Playgroud)

由于您实际上不需要生成输出,并且可能不想打扰视觉渲染输出,您可能还想禁用颜色输出:

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
Run Code Online (Sandbox Code Playgroud)

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

glDisable(GL_CULL_FACE);
Run Code Online (Sandbox Code Playgroud)

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

glScissor(selectionLeft, selectionBottom, selectionWidth, selectionHeight);
glEnable(GL_SCISSOR_TEST);
Run Code Online (Sandbox Code Playgroud)

对于使用遮挡查询的渲染,您需要每个曲面的查询对象:

GLuint queryIds[surfaceCount];
glGenQueries(surfaceCount, queryIds);
Run Code Online (Sandbox Code Playgroud)

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

glBeginQuery(GL_SAMPLES_PASSED, queryIds[k]);
// render surface k
glEndQuery(GL_SAMPLES_PASSED);
Run Code Online (Sandbox Code Playgroud)

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

GLint pixelCounts[surfaceCount];
// for all surfaces k
glGetQueryObjectiv(queryIds[k], GL_QUERY_RESULT, &pixelCounts[k]);
Run Code Online (Sandbox Code Playgroud)

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

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

  • @ AndonM.Coleman我不认为延迟会很糟糕.由于它是为响应用户输入而完成的,因此不会非常频繁地执行.关键是用所有查询提交整个渲染,并且最后只请求查询结果.因此,当您最后开始询问查询结果时,您只有一个同步点.遮挡查询的不良情况是在提交查询后立即阻止结果. (2认同)

And*_*man 5

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

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

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

下面的图说明了很好该测试体积:

   在此输入图像描述