检查ofRay是否与ofMeshFace相交

问题描述 投票:0回答:1

我已尝试过openframeworks论坛和文档中的许多代码示例,但我无法让我做任何我想做的事情。

我有一个ofRay(fromxRay)和一个of3dPrimitive列表。我试图弄清楚光线是否与基元相交,如果是,则要知道光线与“第一”相交的基元(如,哪一个最接近屏幕)。

    void renderer::selectPrimitive(int x, int y, bool shiftHeld)
{
    ofVec3f screenToWorld = (**cam).screenToWorld(ofVec3f(x, y, 0.0));

    primitive* intersectPrim = nullptr;
    int distanceClosest = std::numeric_limits<int>::max();

    ofVec3f vectNow = (screenToWorld - (**cam).getPosition());

    vectNow = vectNow.normalize();

    ofRay ray((**cam).getPosition(), vectNow, true);
    // To draw the ray on screen, for debugging
    // rays.push_back(ray);

    for (primitive& p : *scn)
    {
        if (!shiftHeld)
        {
            p.setSelected(false);
        }

        float* distance = new float(0);

        bool found = p.checkIntersectionPlaneAndLine(ray, distance);
        if (found)// && *distance >= 0 && *distance < distanceClosest)
        {
            intersectPrim = &p;
            //distanceClosest = *distance;
        }
    }

    if (distanceClosest < (std::numeric_limits<int>::max() - 1))
    {
        intersectPrim->setSelected(!intersectPrim->getSelected());
        std::cout << "Selected Primitive" << std::endl;
    }
    else
    {
        std::cout << "Selected Nothing" << std::endl;
    }
}

以下是我尝试过的不同方法,从许多网站上的许多示例中拼凑而成,但它们都没有正常工作。

第一次尝试:

bool primitive3d::calcTriangleIntersection(ofRay ray, float *result) const {

    ofMesh mesh = prim->getMesh();
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces();

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i) {

        ofMeshFace face = *i;

        ofVec3f edge1, edge2, tvec, pvec, qvec;
        float det;
        float u, v;
        const float EPSILON = 0.000001f;

        edge1 = face.getVertex(1) - face.getVertex(0);
        edge2 = face.getVertex(2) - face.getVertex(0);

        pvec = ray.t.getCrossed(edge2);
        det = edge1.dot(pvec);

#if 0 // we don't want to backface cull
        if (det >= EPSILON)
        {
            tvec = getOrigin() - vert0;

            u = tvec.dot(pvec);
            if (!((u < 0.0f) || (u > det)))
            {

                qvec = tvec.getCrossed(edge1);
                v = getDirection().dot(qvec);
                if (!(v < 0.0f || u + v > det))
                {

                    *result = edge2.dot(qvec) / det;
                    return true;
                }
            }
        }
#else
        if (!(det > -EPSILON && det < EPSILON))
        {
            float inv_det = 1.0f / det;
            tvec = ray.s - face.getVertex(0);
            u = tvec.dot(pvec) * inv_det;
            if (!(u < 0.0f || u > 1.0f))
            {

                qvec = tvec.getCrossed(edge1);

                v = ray.t.dot(qvec) * inv_det;
                if (!(v < 0.0f || u + v > 1.0f))
                {

                    *result = edge2.dot(qvec) * inv_det;
                    return true;
                }
            }
        }
#endif
    }
    return false;
}

第二次尝试:

bool primitive3d::checkIntersectionPlaneAndLine(ofRay ray, float *result) const {

    ofMesh mesh = prim->getMesh();
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces();

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i)
    {

        ofMeshFace face = *i;

        ofVec3f P1, P2;
        P1 = ray.getStart();
        P2 = ray.getEnd();

        ofVec3f p1, p2, p3;
        p1 = face.getVertex(0);
        p2 = face.getVertex(1);
        p3 = face.getVertex(2);

        ofVec3f v1 = p1 - p2;
        ofVec3f v2 = p3 - p2;

        float a, b, c, d;

        a = v1.y * v2.z - v1.z * v2.y;
        b = -(v1.x * v2.z - v1.z * v2.x);
        c = v1.x * v2.y - v1.y * v2.x;
        d = -(a * p1.x + b * p1.y + c * p1.z);

        ofVec3f O = P1;
        ofVec3f V = P2 - P1;

        float t;

        t = -(a * O.x + b * O.y + c * O.z + d) / (a * V.x + b * V.y + c * V.z);

        ofVec3f p = O + V * t;

        float xmin = std::min(P1.x, P2.x);
        float ymin = std::min(P1.y, P2.y);
        float zmin = std::min(P1.z, P2.z);

        float xmax = std::max(P1.x, P2.x);
        float ymax = std::max(P1.y, P2.y);
        float zmax = std::max(P1.z, P2.z);


        if (inside(p, xmin, xmax, ymin, ymax, zmin, zmax)) {
            *result = p.length();
            return true;
        }
    }
    return false;
}

bool primitive3d::inside(ofVec3f p, float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) const {

    if (p.x >= xmin && p.x <= xmax && p.y >= ymin && p.y <= ymax && p.z >= zmin && p.z <= zmax)
        return true;

    return false;

}

第三次尝试:

#define SMALL_NUM   0.00000001 // anything that avoids division overflow
// dot product (3D) which allows vector operations in arguments
#define dot(u,v)   ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)

bool primitive3d::checkIntersectionTriangleRay(ofRay ray, ofPoint* inter)
{
    ofMesh mesh = prim->getMesh();
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces();

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i)
    {
        ofMeshFace triangle = *i;

        ofVec3f   u, v, n;              // Vecs of triangle
        ofVec3f   dir, w0, w;           // Vecs of ofRay
        float     r, a, b;              // params to calc ray-plane intersect

                                        // get triangle edge vectors and plane normal
        u = triangle.getVertex(1) - triangle.getVertex(0);
        v = triangle.getVertex(2) - triangle.getVertex(0);
        n = u * v;              // cross product
        if (!(n == ofVec3f(0, 0, 0)))           // if triangle is not degenerate
        {

            dir = ray.getEnd() - ray.getStart();              // ray direction vector
            w0 = ray.getStart() - triangle.getVertex(0);
            a = -dot(n, w0);
            b = dot(n, dir);
            if (!(fabs(b) < SMALL_NUM))
            {     // if ray is not parallel to triangle

                // get intersect point of ray with triangle plane
                r = a / b;
                if (!(r < 0.0))                    // ray goes toward the triangle
                {
                    // for a segment, also test if (r > 1.0) => no intersect

                    *inter = ray.getStart() + r * dir;            // intersect point of ray and plane

                                                    // is I inside T?
                    float    uu, uv, vv, wu, wv, D;
                    uu = dot(u, u);
                    uv = dot(u, v);
                    vv = dot(v, v);
                    w = *inter - triangle.getVertex(0);
                    wu = dot(w, u);
                    wv = dot(w, v);
                    D = uv * uv - uu * vv;

                    // get and test parametric coords
                    float s, t;
                    s = (uv * wv - vv * wu) / D;
                    if (!(s < 0.0 || s > 1.0))         // I is inside T
                    {
                        t = (uv * wu - uu * wv) / D;
                        if (!(t < 0.0 || (s + t) > 1.0))  // I is inside T
                            return true;                       // I is in T
                    }
                }
            }
        }
    }
    return false;
}

我尝试了很多东西,但都没有。我也把我的光线吸引到屏幕上,所以我知道它们确实是正确创造的并且朝着正确的方向前进了无限距离

为了清楚起见,我删除了很多代码以使其易于阅读。我在第二种方法中只缺少// Detection Here部分,因为我不知道如何使其工作。

c++ c++11 3d openframeworks
1个回答
1
投票

假设互联网上有大量的三角形/网状光线投射代码,你试过会解决你的问题,我认为你的问题是在第一种方法中,你设置一个“找到”变量,但如果找到或者不从方法返回用最小距离挑选物体。

您提供的代码将返回最后一个原语的命中测试结果。

如果您的代码过度简化,请再次发布详细信息。

编辑:

从网格面部获得的坐标位于对象空间中。您需要在进行任何计算之前将它们转换为世界空间,或者更好地将光线转换为对象空间。

请参阅此代码以了解有效的实现:https://github.com/mrdoob/three.js/blob/master/src/objects/Mesh.js

请注意applyMatrix4调用应用世界矩阵将它们带到同一空间。

© www.soinside.com 2019 - 2024. All rights reserved.