Triangle intersection ray space with two algorithms achieve

1 Overview

Any complex three-dimensional models can be regarded as a set of triangular facets space, a problem is likely to encounter the problem that the space ray intersects with the triangle, e.g. pickup, masking detection. Here are summarized under two algorithm to the problem.

2. General algorithm

A very conventional idea is to calculate the ray intersection of triangular facets, whether to look at the intersection point inside the triangle.

2.1. Theoretical derivation

For a ray space, so that the starting point is O, which direction is D, according to the parameters of formula rays, on which an arbitrary point P (which is required to the intersection) is:
\ [P = O + tD \ Tag. 1} {\]

Where t> 0, depending on the value of t, we can get a different point on the ray, i.e. the key is unknown evaluation of t.

Known spatial triangular patches three vertices v1, v2, v3, it can be determined easily triangular facets of the normal vector n. Obviously vectors (v1-P) with the plane n is perpendicular, their dot product is 0:
\ [(v1-P) \ n = 0 CDOT \ {2} Tag \]

The formula (1) into equation (2), t is calculated unknowns:
\ [t = \ FRAC {D \ n-CDOT} {\ n-CDOT (V1-O)} \]

T and then substituting into (1) wherein, to obtain plane and the three rays of the composition.

The next step is determining whether the intersection point inside the triangle of the surface, because it is triangular space, it is better algorithm in [2] mentioned the same method, are summarized as follows:

With the law

2.2. Specific realization

The specific C / C ++ codes are as follows:

#include <iostream>

using namespace std;

#define EPSILON 0.000001

// 3D vector
class Vector3d
{
public:
    Vector3d()
    {
    }

    ~Vector3d()
    {
    }

    Vector3d(double dx, double dy, double dz)
    {
        x = dx;
        y = dy;
        z = dz;
    }

    // 矢量赋值
    void set(double dx, double dy, double dz)
    {
        x = dx;
        y = dy;
        z = dz;
    }

    // 矢量相加
    Vector3d operator + (const Vector3d& v) const
    {
        return Vector3d(x + v.x, y + v.y, z + v.z);
    }

    // 矢量相减
    Vector3d operator - (const Vector3d& v) const
    {
        return Vector3d(x - v.x, y - v.y, z - v.z);
    }

    //矢量数乘
    Vector3d Scalar(double c) const
    {
        return Vector3d(c*x, c*y, c*z);
    }

    // 矢量点积
    double Dot(const Vector3d& v) const
    {
        return x * v.x + y * v.y + z * v.z;
    }

    // 矢量叉积
    Vector3d Cross(const Vector3d& v) const
    {
        return Vector3d(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
    }

    double _x()
    {
        return x;
    }

    double _y()
    {
        return y;
    }

    double _z()
    {
        return z;
    }

private:
    double x, y, z;
};

// v1 = Cross(AB, AC)
// v2 = Cross(AB, AP)
// 判断矢量v1和v2是否同向
bool SameSide(Vector3d A, Vector3d B, Vector3d C, Vector3d P)
{
    Vector3d AB = B - A;
    Vector3d AC = C - A;
    Vector3d AP = P - A;
    
    Vector3d v1 = AB.Cross(AC);
    Vector3d v2 = AB.Cross(AP);

    // v1 and v2 should point to the same direction
    //return v1.Dot(v2) >= 0 ;
    return v1.Dot(v2) > 0;
}

// 判断点P是否在三角形ABC内(同向法)
bool PointinTriangle1(Vector3d A, Vector3d B, Vector3d C, Vector3d P)
{
    return SameSide(A, B, C, P) && SameSide(B, C, A, P) && SameSide(C, A, B, P);
}

//ray-triangle intersection algorithm  (通过平面方程计算)
//参数说明:V1,V2,V3,三角形三点;O,射线原点;D,射线方向
bool ray_triangle_intersection1(Vector3d V1, Vector3d V2, Vector3d V3, Vector3d O, Vector3d D, Vector3d *I)
{
    bool rv = false;

    //v1(n1,n2,n3);
    //平面方程: na * (x – n1) + nb * (y – n2) + nc * (z – n3) = 0 ;
    double na = (V2._y() - V1._y())*(V3._z() - V1._z()) - (V2._z() - V1._z())*(V3._y() - V1._y());
    double nb = (V2._z() - V1._z())*(V3._x() - V1._x()) - (V2._x() - V1._x())*(V3._z() - V1._z());
    double nc = (V2._x() - V1._x())*(V3._y() - V1._y()) - (V2._y() - V1._y())*(V3._x() - V1._x());

    //平面法向量
    Vector3d nv(na, nb, nc);

    //平面法向量与射线方向向量差积
    double vpt = D.Dot(nv);
    if (vpt == 0)
    {
        rv = false;  //此时直线与平面平行
    }
    else
    {
        Vector3d P = V1 - O;
        double t = P.Dot(nv) / vpt;

        *I = O + D.Scalar(t);
        if (PointinTriangle1(V1, V2, V3, *I))
        {
            rv = true;
        }
        else
        {
            rv = false;
        }
    }

    return rv;
}

int main()
{
    Vector3d V1(0, 0, 0);
    Vector3d V2(50, 0, 0);
    Vector3d V3(0, 50, 0);
    Vector3d O(5, 10, -10);
    Vector3d P(10, 10, 10);
    Vector3d D = P - O;
    Vector3d I;

    if (ray_triangle_intersection1(V1, V2, V3, O, D, &I)) {
        cout << I._x() << '\t' << I._y() << '\t' << I._z() << endl;
    }   
}

3. Optimization

Thinking carefully about conventional algorithm, when calculating the point of intersection of the ray and the plane, actually the parametric equation parameters associated with the plane equation rays legislation seeking value. So if you know the parameter space equation triangle, with its simultaneous equations ray parameters, you can not directly obtain the intersection yet? Tomas Moller's paper "Fast, Minimum Storage Ray Triangle Intersection" proposes an optimization algorithm is based on this idea, and gives a reasonable solution.

3.1. Theoretical derivation

For the three vertices V1, V2, V3 space consisting of triangular, to any point within the triangle, with the following parametric equation:
\ [P = (. 1-UV) + Vl + uV2 VV3 \ Tag. 3} {\]

u, v is the heavy weight V2 and V3, 1-uv is the weight of the weight V1, and satisfies u> = 0, v> = 0, u + v <= 1. Specific parameter equation may explain the Reference [5], are summarized as follows:

Triangular parametric equation

The ray equations (1) and the triangle equation (3) simultaneous up with:
\ [(UV-1) + Vl + uV2 VV3 tD = O + \]

Obviously, u, v, t are unknown, transposition and finishing of linear equations can be obtained as follows:
\ [\ left [\ the begin {-D} Matrix & Vl-V2-V3 & Vl \ Matrix End {} \ right] \ left [\ begin { matrix} t \\ u \\ v \ end {matrix} \ right] = O - V1 \]

Can use Cramer's rule to solve the system of linear equations, we can review Linear Algebra ([6]), which I have here summarized as follows:

Cramer's rule

Order \ (E1 = V2 - Vl, V3 = E2 of - Vl, T = O - Vl \) , then the above equation can be rewritten as:
\ [\ left [\} -D the begin {& Matrix E1 & E2 of \ End {Matrix } \ right] \ left [\ begin {matrix} t \\ u \\ v \ end {matrix} \ right] = T \]

根据克莱姆法则,有:
\[\begin{cases} t = \frac{1}{\begin{vmatrix} -D&E1&E2\\ \end{vmatrix}} \begin{vmatrix} T&E1&E2\\ \end{vmatrix} \\ u = \frac{1}{\begin{vmatrix} -D&E1&E2\\ \end{vmatrix}} \begin{vmatrix} -D&T&E2\\ \end{vmatrix} \\ v = \frac{1}{\begin{vmatrix} -D&E1&E2\\ \end{vmatrix}} \begin{vmatrix} -D&E1&T\\ \end{vmatrix} \\ \end{cases} \]

接下来就要用到向量的混合积公式(具体可参看文献[7])了,对于三向量a,b,c,有:
\[ \begin{vmatrix} a&b&c\\ \end{vmatrix} = a \times b \cdot c = - a \times c \cdot b = - c \times b \cdot a \]
上式可改写成:
\[\begin{cases} t = \frac{1}{D \times E2 \cdot E1} (T \times E1 \cdot E2) \\ u = \frac{1}{D \times E2 \cdot E1} (D \times E2 \cdot T) \\ v = \frac{1}{D \times E2 \cdot E1} (T \times E1 \cdot D) \\ \end{cases} \]
\(P=D \times E2, Q = T \times E1\),进一步简化可得:
\[\begin{cases} t = \frac{1}{P \cdot E1} (Q \cdot E2) \\ u = \frac{1}{P \cdot E1} (P \cdot T) \\ v = \frac{1}{P \cdot E1} (Q \cdot D) \\ \end{cases} \]

3.2. 具体实现

具体的C/C++实现代码如下:

#include <iostream>

using namespace std;

#define EPSILON 0.000001

// 3D vector
class Vector3d
{
public:
    Vector3d()
    {
    }

    ~Vector3d()
    {
    }

    Vector3d(double dx, double dy, double dz)
    {
        x = dx;
        y = dy;
        z = dz;
    }

    // 矢量赋值
    void set(double dx, double dy, double dz)
    {
        x = dx;
        y = dy;
        z = dz;
    }

    // 矢量相加
    Vector3d operator + (const Vector3d& v) const
    {
        return Vector3d(x + v.x, y + v.y, z + v.z);
    }

    // 矢量相减
    Vector3d operator - (const Vector3d& v) const
    {
        return Vector3d(x - v.x, y - v.y, z - v.z);
    }

    //矢量数乘
    Vector3d Scalar(double c) const
    {
        return Vector3d(c*x, c*y, c*z);
    }

    // 矢量点积
    double Dot(const Vector3d& v) const
    {
        return x * v.x + y * v.y + z * v.z;
    }

    // 矢量叉积
    Vector3d Cross(const Vector3d& v) const
    {
        return Vector3d(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
    }

    double _x()
    {
        return x;
    }

    double _y()
    {
        return y;
    }

    double _z()
    {
        return z;
    }

private:
    double x, y, z;
};

//ray-triangle intersection algorithm
//参数说明:V1,V2,V3,三角形三点;O,射线原点;D,射线方向。
bool ray_triangle_intersection(Vector3d V1, Vector3d V2, Vector3d V3, Vector3d O, Vector3d D, Vector3d *I)
{
    //Find vectors for two edges sharing V1
    Vector3d e1 = V2 - V1;
    Vector3d e2 = V3 - V1;

    //Begin calculating determinant - also used to calculate u parameter
    Vector3d P = D.Cross(e2);
    //if determinant is near zero, ray lies in plane of triangle
    double det = e1.Dot(P);
    //NOT CULLING
    if (det > -EPSILON && det < EPSILON)
    {
        return false;
    }
    double inv_det = 1.f / det;

    //calculate distance from V1 to ray origin
    Vector3d T = O - V1;

    //Calculate u parameter and test bound
    double u = T.Dot(P) * inv_det;
    //The intersection lies outside of the triangle
    if (u < 0.f || u > 1.f)
    {
        return false;
    }

    //Prepare to test v parameter
    Vector3d Q = T.Cross(e1);
    //Calculate V parameter and test bound
    double v = D.Dot(Q) * inv_det;

    //The intersection lies outside of the triangle
    if (v < 0.f || u + v  > 1.f)
    {
        return false;
    }

    double t = e2.Dot(Q) * inv_det;

    //ray intersection
    if (t > EPSILON)
    {
        *I = O + D.Scalar(t);
        return true;
    }

    return false;
}

int main()
{
    Vector3d V1(0, 0, 0);
    Vector3d V2(50, 0, 0);
    Vector3d V3(0, 50, 0);
    Vector3d O(5, 10, -10);
    Vector3d P(10, 10, 10);
    Vector3d D = P - O;
    Vector3d I;

    if (ray_triangle_intersection(V1, V2, V3, O, D, &I)) {
        cout << I._x() << '\t' << I._y() << '\t' << I._z() << endl;
    }
}

可以看到这种优化算法无论是代码量还是时间、空间复杂度都由于原来的常规算法,最直观的体现就是判断语句多,能够即使返回避免后续运算。

4. 参考

[. 1] Möller-Trumbore intersection algorithm
[2] is determined point is inside the triangle
[3] ray intersects the detector plane (Ray-Plane intersection Test)
[. 4] Intersection Test rays and triangles (ray Triangle intersection Test)
[. 5 ] triangle equation? - Step Takasaki Ting answer - almost known
[6] Cramer's rule
[7] Mixed Products Three Vector

Guess you like

Origin www.cnblogs.com/charlee44/p/12318605.html