Ray和Triangle Edge如何相交?

时间:2016-08-05 16:18:50

标签: algorithm math 3d intersection ray-picking

我正面临着与三角形边缘相交的问题。实际上,我正在尝试使用鼠标选择/交叉网格的三角形,顶点,边缘。所以我从鼠标当前位置制作了光线,然后我将它与网格元素(如三角形/多边形,顶点,边缘等)相交以使用它。基本上,3D建模的东西。与三角形相交很简单有趣。顶点部分很棘手。

但现在,我不知道如何用三角形边相交/拾取。我的意思是我在与鼠标射线相交时如何对待它们?首先,我认为它们可以像3D线一样对待。但最终未能做到Ray和the Line相交。在互联网上搜索但没有找到任何有用的信息。虽然我发现一些开源项目正在使用OpenGL内置的拾取功能来选择/与Edge交叉。但就我而言,我无法使用它。 :(

我当前的边缘选择代码结构如下所示:

void pickEdge(Ray ray, Scene scene)
{
    for each object in scene
    {
        mesh = getMesh(object)
        for each triangle in mesh
        {
            for each edge in triangle
            {
                v1 = getV1(edge)
                v2 = getV2(edge)

                // Do intersect with 'ray' and 'v1', 'v2'. But how?
            }
        }
    }
}

所以我被困在这里,真的需要一些帮助。非常感谢任何想法,算法或小帮助。

4 个答案:

答案 0 :(得分:1)

请查看本页末尾的算法以及本网站提供的所有算法:http://geomalgorithms.com/a05-_intersect-1.html

答案 1 :(得分:1)

在您的情况下,在3D空间中找到三角形和光线之间的交点的问题可以归结为在2D空间(平面)中以三角形查找点位置(INSIDE,OUTSIDE,ON BOUNDARY)。您应该做的就是在屏幕平面上投影三角形,在边缘找到交叉点并在边缘上执行反向投影。点的位置是鼠标的位置。唯一的问题是处理退化情况,如将三角形映射到线段。但我认为这不会有问题,因为这种情况很容易应对。

答案 2 :(得分:1)

第一种方法是将边缘(和光线)正交投影到垂直于光线的平面,然后计算投影光线到投影边缘的距离。

即,首先确定与光线正交的两个正交矢量rdir1, rdir2

然后你计算你的光线(它的基点)到这个平面的投影,这将产生一个2d点rp

然后通过简单地应用点积来将边缘投影到该平面: pv1 = new Vector2(DotProduct(v1, rdir1), DotProduct(v1, rdir2)) pv2 = new Vector2(DotProduct(v2, rdir1), DotProduct(v2, rdir2))

现在,您可以计算从第2行pv1, pv2到点rp的距离。

如果边缘的方向取自视图矩阵的“向前”方向,则与其正交的两个向量将是视图矩阵的左右向量。

执行上述配方将产生类似于将边缘投影到屏幕的效果。因此,您也可以将边缘投影到屏幕并使用这些坐标。

答案 3 :(得分:0)

首先,两个几何对象A和B之间的距离是多少?它是A和B上任意两点之间的最小距离,即。 dist(A,B) = min { EuclideanLength(x - y) | x in A, y in B}。 (如果它存在并且是唯一的,它就是你的情况。)

您已经知道EuclideanLength((x,y,z)) = sqrt(x^2 + y^2 + z^2)。由于sqrt严格增加,因此最小化SquareEuclideanLength((x,y,z)) = x^2 + y^2 + z^2就足够了,这极大地简化了问题。

在您的问题中,对象是线段A := {v1 + t*(v2-v1) | 0 <= t <= 1}和线B := {p + s*d | s is any real number}。 (不要担心你问过一条光线,一条线就是你想要的。)

现在计算距离归结为找到合适的ts,使SquareEuclideanLength(v1 + t*(v2-v1) - p - s*d)最小,然后计算EuclideanLength(v1 + t*(v2-v1) - p - s*d)以获得真实距离。

要解决这个问题,我们需要一些解析几何。由于d不为零,我们可以将每个向量v写为与d正交的部分和d的倍数的部分之和:{ {1}}。对于这种“正交分解”,它始终保持v = Ov + Mv

由于上面的SquareEuclideanLength(v) = SquareEuclideanLength(Ov) + SquareEuclideanLength(Mv)

d = Md

左侧加数不依赖于SquareEuclideanLength(v1 + t*(v2-v1) - p - s*d) = SquareEuclideanLength(Ov1 + t*(Ov2-Ov1) - Op) + SquareEuclideanLength(Mv1 + t*(Mv2-Mv1) - Mp - s*d),但是您选择了s,您可以找到t,使得正确的加数为0! (请记住,sMv1,...是Mv2的倍数。)

因此,为了找到最小值,您只需要找到上述地图dO,然后找到最小化器M

假设t已归一化,这些实际上由dOv := CrossProduct(v, d)提供,但请相信我,如果Mv := DotProduct(v, d)*d未规范化,这也有效。< / p>

现在找到距离的方法是:找到最小化的d

0 <= t <= 1

你已经从点线距离计算中了解了这个公式(就是这样),并通过区分SquareEuclideanLength(Cross(v1 - p, d) + t*Cross(v2 - v1, d)) = SquareEuclideanLength(Cross(v1 - p, d)) + 2*t*Dot(Cross(v1 - p, d), Cross(v2 - v1, d)) + t^2 SquareEuclideanLength(Cross(v2 - v1, d))和等于0来解决。

所以这个等式的最小化是 t

使用此t = -Dot(Cross(v1 - p, d), Cross(v2 - v1, d))/SquareEuclideanLength(Cross(v2 - v1, d))计算t,线段A上距离线B最近的点,您可以将其插入点线距离算法中以查找所需的距离。

我希望这可以帮到你!