OpenGL GL_SELECT 还是手动碰撞检测?
如图所示
我绘制了一组轮廓(多边形)作为 GL_LINE_STRIP。 现在我想在3D中选择鼠标下的曲线(多边形)进行删除、移动等。
我想知道使用哪种方法:
1.使用OpenGL拾取和选择。 ( glRenderMode(GL_SELECT) )
2.使用手动碰撞检测,通过使用拾取射线并检查射线是否在每个多边形内部。
As seen in the image
I draw set of contours (polygons) as GL_LINE_STRIP.
Now I want to select curve(polygon) under the mouse to delete,move..etc in 3D .
I am wondering which method to use:
1.use OpenGL picking and selection. ( glRenderMode(GL_SELECT) )
2.use manual collision detection , by using a pick-ray and check whether the ray is inside each polygon.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我强烈建议不要使用 GL_SELECT。这种方法非常古老,在新的 GL 版本中不存在,并且您可能会在使用现代显卡时遇到问题。不要指望它会受到硬件支持 - 可能您会在许多 GPU 上遇到此模式的软件(驱动程序)回退,前提是它可以正常工作。使用风险自负:)
让我为您提供一个替代方案。
对于实体、大的对象,有一个古老的、好的选择方法:
这为您提供了一种非常可靠的“按对象”拾取方法。另外,以最少的每像素操作绘制和清除 1 个像素不会真正损害您的性能,除非您的顶点处理能力不足(我认为不太可能)或者有很多对象并且可能会占用 CPU-限制绘制调用的数量(但话又说回来,我相信如果您可以将颜色作为每像素数据传递,则可以将其优化为单个绘制调用)。
RGB 中的颜色是 3 个无符号字节,但应该可以额外使用帧缓冲区的 alpha 通道作为最后一个字节,因此总共会得到 4 个字节 - 足以存储任何指向对象的 32 位指针:颜色。
或者,您可以为此创建具有特定像素格式的专用帧缓冲区对象(例如 GL_R32UI ,或者如果需要 64 位,甚至是 GL_RG32UI )。
上述是严格几何方法的一个很好且快速的替代方案(无论是在可靠性方面还是在实现时间方面)。
I strongly recommend against GL_SELECT. This method is very old and absent in new GL versions, and you're likely to get problems with modern graphics cards. Don't expect it to be supported by hardware - probably you'd encounter a software (driver) fallback for this mode on many GPUs, provided it would work at all. Use at your own risk :)
Let me provide you with an alternative.
For solid, big objects, there's an old, good approach of selection by:
This gives you a very reliable "per-object" picking method. Also, drawing and clearing only 1 pixel with minimal per-pixel operation won't really hurt your performance, unless you are short on vertex processing power (unlikely, I think) or have really a lot of objects and are likely to get CPU-bound on the number of draw calls (but then again, I believe it's possible to optimize this away to a single draw call if you could pass the colour as per-pixel data).
The colour in RGB is 3 unsigned bytes, but it should be possible to additionally use the alpha channel of the framebuffer for the last byte, so you'd get 4 bytes in total - enough to store any 32-bit pointer to the object as the colour.
Alternatively, you can create a dedicated framebuffer object with a specific pixel format (like
GL_R32UI
, or evenGL_RG32UI
if you need 64 bits) for that.The above is a nice and quick alternative (both in terms of reliability and in implementation time) for the strict geometric approach.
我发现在新的 GPU 上,GL_SELECT 模式非常慢。我尝试了几种不同的方法来解决这个问题。
第一个是进行 CPU 碰撞测试,该测试有效,但速度没有我希望的那么快。当您将光线投射到屏幕上(使用 gluUnproject)然后尝试查找鼠标正在与哪个对象发生碰撞时,它肯定会减慢速度。我获得满意速度的唯一方法是使用八叉树来减少碰撞测试的数量,然后进行边界框碰撞测试 - 然而,这导致了一种像素不完美的方法。
我决定的方法是首先找到鼠标下的所有对象(使用 gluUnproject 和边界框碰撞测试),这通常非常快。然后,我将后缓冲区中可能与鼠标发生碰撞的每个对象渲染为不同的颜色。然后我使用 glReadPixel 获取鼠标下方的颜色,并将其映射回对象。 glReadPixel 是一个缓慢的调用,因为它必须从帧缓冲区读取。然而,每帧完成一次,最终花费的时间可以忽略不计。如果您愿意,您可以通过渲染到 PBO 来加快速度。
吉瓦
I found that on new GPUs, the GL_SELECT mode is extremely slow. I played with a few different ways of fixing the problem.
The first was to do a CPU collision test, which worked, but wasn't as fast as I would have liked. It definitely slows down when you are casting rays into the screen (using gluUnproject) and then trying to find which object the mouse is colliding with. The only way I got satisfactory speeds was to use an octree to reduce the number of collision tests down and then do a bounding box collision test - however, this resulted in a method that was not pixel perfect.
The method I settled on was to first find all the objects under the mouse (using gluUnproject and bounding box collision tests) which is usually very fast. I then rendered each of the objects that have potentially collided with the mouse in the backbuffer as a different color. I then used glReadPixel to get the color under the mouse, and map that back to the object. glReadPixel is a slow call, since it has to read from the frame buffer. However, it is done once per frame, which ends up taking a negligible amount of time. You can speed it up by rendering to a PBO if you'd like.
Giawa
umanga,看不到如何内联回复...也许我应该注册:)
首先,我必须为给你错误的算法而道歉 - 我做了背面剔除算法。但你需要的非常相似,这就是为什么我感到困惑......天哪。
如前所述,获取相机位置到鼠标向量。
对于每个轮廓,循环遍历其中的所有坐标对(0-1, 1-2, 2-3, ... n-0),并像以前一样将它们制成一个 vec。即沿着轮廓行走。
现在对这两个(鼠标向量的轮廓边缘)进行交叉积,而不是像我之前所说的那样在对之间进行交叉积,对所有对进行交叉积,并将它们全部加起来。
最后求出结果向量的大小。如果结果为零(考虑到舍入误差),则您的形状位于外部 - 无论面向如何。如果您对面感兴趣,那么您可以使用鼠标矢量进行点刺来找到面并测试符号 +/-,而不是使用 mag。
它之所以有效,是因为该算法依次找到从矢量线到每个点的距离。当你把它们加起来并且你在外面时,它们就会全部抵消,因为轮廓是闭合的。如果你内心深处,那么它们就全部总结起来了。它实际上是物理学中电磁场的高斯定律...
请参阅:http://en.wikipedia.org/wiki/Gauss%27s_law 并注意“方程的右侧是 S 包含的总电荷除以电常数”,注意“封闭”一词 - 即零意味着不封闭。
您仍然可以使用边界框进行优化以提高速度。
umanga, Cant see how to reply inline... maybe I should sign up :)
First of all I must apologize for giving you the wrong algo - i did the back face culling one. But the one you need is very similar which is why I got confused... d'oh.
Get the camera position to mouse vector as said before.
For each contour, loop through all the coords in pairs (0-1, 1-2, 2-3, ... n-0) in it and make a vec out of them as before. I.e. walk the contour.
Now do the cross prod of those two (contour edge to mouse vec) instead of between pairs like I said before, do that for all the pairs and vector add them all up.
At the end find the magnitude of the resulting vector. If the result is zero (taking into account rounding errors) then your outside the shape - regardless of facing. If your interested in facing then instead of the mag you can do that dot prod with the mouse vector to find the facing and test the sign +/-.
It works because the algo finds the amount of distance from the vector line to each point in turn. As you sum them up and you are outside then they all cancel out because the contour is closed. If your inside then they all sum up. Its actually Gauss's Law of electromagnetic fields in physics...
See:http://en.wikipedia.org/wiki/Gauss%27s_law and note "the right-hand side of the equation is the total charge enclosed by S divided by the electric constant" noting the word "enclosed" - i.e. zero means not enclosed.
You can still do that optimization with the bounding boxes for speed.
过去,我使用 GL_SELECT 来确定哪些对象贡献了感兴趣的像素,然后使用计算几何来获得与对象的准确交集(如果需要)。
In the past I've used GL_SELECT to determine which object(s) contributed the pixel(s) of interest and then used computational geometry to get an accurate intersection with the object(s) if required.
您希望通过单击轮廓(在边缘上)还是多边形的内部来进行选择?您的第二种方法听起来像是您希望在内部单击以选择最紧密的包含多边形。我认为渲染
GL_LINE_STRIP
后的GL_SELECT
不会使内部响应点击。如果这是一个真正的等高线图(从图像来看,我认为不是,边缘似乎相交),那么将有一个更简单的算法可用。
Do you expect to select by clicking the contour (on the edge) or the interior of the polygon? Your second approach sounds like you want clicks in the interior to select the tightest containing polygon. I don't think that
GL_SELECT
after renderingGL_LINE_STRIP
is going to make the interior responsive to clicks.If this was a true contour plot (from the image I don't think it is, edges appear to intersect) then a much simpler algorithm would be available.
如果您留在线条中,则无法使用选择,因为您必须单击渲染的线条像素,而不是单击包围它们的线条内的空间,我将其视为您希望执行的操作。
您可以使用科斯的答案,但为了渲染空间,您需要固体填充它,这将涉及将所有轮廓转换为凸类型,这是痛苦的。所以我认为这有时会起作用,但在某些情况下会给出错误的答案,除非你这样做。
你需要做的就是使用CPU。您拥有视口和透视矩阵的视图范围。使用鼠标坐标,生成视图到鼠标指针向量。您还拥有轮廓的所有坐标。
取第一个轮廓的第一个坐标,并创建一个到第二个坐标的向量。用它们制作一个向量。取第三个坐标并创建一个从 2 到 3 的向量,并围绕轮廓重复一遍,最后再次将坐标 n 的最后一个向量返回到 0。对于序列中的每一对,求叉积并对所有结果求和。当您获得最终求和向量时,请保留该向量并与鼠标指针方向向量进行点积。如果它的+ve那么鼠标在轮廓内,如果它的-ve那么它不在轮廓内,如果0那么我猜轮廓的平面和鼠标方向是平行的。
对每个轮廓执行此操作,然后您就会知道其中哪些轮廓被鼠标尖刺了。您想从该集合中选择哪一个取决于您。最高 Z ?
听起来工作量很大,但还不错,并且会给出正确的答案。您可能还想保留所有轮廓的边界框,然后您可以通过对完整向量执行相同的数学运算,提前将鼠标向量中的边界框取出,但仅在 4 条边上,如果它不在内部,则轮廓不能是任何一个。
You cant use select if you stay with the lines because you would have to click on the line pixels rendered not the space inside the lines bounding them which I read as what you wish to do.
You can use Kos's answer but in order to render the space you need to solid fill it which would involve converting all of your contours to convex types which is painful. So I think that would work sometimes and give the wrong answer in some cases unless you did that.
What you need to do is use the CPU. You have the view extents from the viewport and the perspective matrix. With the mouse coord, generate the view to mouse pointer vector. You also have all the coords of the contours.
Take the first coord of the first contour and make a vector to the second coord. Make a vector out of them. Take 3rd coord and make a vector from 2 to 3 and repeat all the way around your contour and finally make the last one from coord n back to 0 again. For each pair in sequence find the cross product and sum up all the results. When you have that final summation vector keep hold of that and do a dot product with the mouse pointer direction vector. If its +ve then the mouse is inside the contour, if its -ve then its not and if 0 then I guess the plane of the contour and the mouse direction are parallel.
Do that for each contour and then you will know which of them are spiked by your mouse. Its up to you which one you want to pick from that set. Highest Z ?
It sounds like a lot of work but its not too bad and will give the right answer. You might like to additionally keep bounding boxes of all your contours then you can early out the ones off of the mouse vector by doing the same math as for the full vector but only on the 4 sides and if its not inside then the contour cannot be either.
第一个是易于实施且广泛使用。
The first is easy to implement and widely used.