You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Möller–Trumbore算法判断顶点是否在模型内的异常问题

问题描述

我正在用C++编写一个程序,该程序生成顶点网格并导入STL模型。随后对网格中的每个顶点使用Möller–Trumbore相交算法,统计从顶点射出的射线与STL模型三角形的相交次数,若顶点在STL模型内部则将其删除。

我的问题是:位于模型边缘的部分(非全部)顶点未被标记为模型内部顶点。

我采用的方法参考了Stack Overflow上的「判断点是否在3D网格内」的算法,并做了补充,确保边缘和顶点相交仅计为1次。

删除内部顶点后的20x20x20立方体网格效果
立方体网格俯视图

代码实现

mesh.cpp 文件

#include "mesh.h"

using namespace std;

bool Mesh::RayIntersectsTriangle(vector3D<double> rayOrigin, 
                           vector3D<double> rayVector, 
                           Triangle* inTriangle,
                           vector3D<double>& outIntersectionPoint)
{
    const double EPSILON = 0.00000000001;
    vector3D<double> vertex0 = inTriangle->corner[0];
    vector3D<double> vertex1 = inTriangle->corner[1];  
    vector3D<double> vertex2 = inTriangle->corner[2];
    vector3D<double> edge1, edge2, h, s, q;
    double a, f, u, v;
    edge1 = vertex1 - vertex0;
    edge2 = vertex2 - vertex0;
    h = rayVector ^ edge2; //^ is cross product
    a = edge1*h; // * is dot product

    if (a > -EPSILON && a < EPSILON)
        return false;    // This ray is parallel to this triangle.

    f = 1.0 / a;
    s = rayOrigin - vertex0;
    u = f * s*h;

    if (u < 0.0 || u > 1.0)
        return false;

    q = s^edge1;
    v = f * rayVector*q;

    if (v < 0.0 || u + v > 1.0)
        return false;

    // At this stage we can compute t to find out where the intersection point is on the line.
    double t = f * edge2*q;

    if (t > EPSILON) // ray intersection
    {
        outIntersectionPoint = rayOrigin + rayVector * t;
        return true;
    }
    else // This means that there is a line intersection but not a ray intersection.
        return false;
}

//Flatten along Z axis so that I can only take into account triangles that are relatively close to my target vertex
void Mesh::check_bounding_box_collision(vector3D<double> target){
    for (const auto & trig : triangles){
        double xmax = max(max(trig.corner[0].x, trig.corner[1].x), trig.corner[2].x);
        double ymax = max(max(trig.corner[0].y, trig.corner[1].y), trig.corner[2].y);
        double xmin = min(min(trig.corner[0].x, trig.corner[1].x), trig.corner[2].x);
        double ymin = min(min(trig.corner[0].y, trig.corner[1].y), trig.corner[2].y);

        if ((target.x <= xmax && target.x >= xmin) && (target.y <= ymax && target.y >= ymin)){
            the_chosen_ones.push_back(trig);
        }

    }
}

void Mesh::displace(vector3D<double> distance){
    for (int i = 0; i < triangles.size(); ++i){
        triangles.at(i).corner[0] += distance;
        triangles.at(i).corner[1] += distance;
        triangles.at(i).corner[2] += distance;
    }
}

unsigned int Mesh::count_intersections(vector3D<double> target){
    unsigned int intersections = 0;
    int blocked = 0;
    vector3D<double> iPoint;
    set<vector3D<double>, Vector3DComparator> intersection_points;
    the_chosen_ones.clear();
    check_bounding_box_collision(target);
    for (auto& triangle : the_chosen_ones){
        if (RayIntersectsTriangle(target, {0.0, 0.0, 1.0}, &triangle, iPoint)) {//Shoot up beam along flattened axis
            if (intersection_points.find(iPoint) == intersection_points.end()) {//If this intersection point exists in the list, I've hit an edge, so I dont count the intersection for a second time.
                intersections++;
                intersection_points.insert(iPoint);
            }
        }
    }

    return intersections;
}


void Mesh::load_stl(string filename){
    stl_reader::StlMesh<double, unsigned int> mesh(filename);
    cout << "Loading " << filename << "..." << endl;

    for(size_t itri = 0; itri < mesh.num_tris(); ++itri) {
        Triangle t;
        for(size_t icorner = 0; icorner < 3; ++icorner) {
            const double* c = mesh.tri_corner_coords (itri, icorner);
            t.corner[icorner] = {c[0], c[1], c[2]};
        }
    
        const double* n = mesh.tri_normal (itri);
        t.normal = {n[0], n[1], n[2]};
        triangles.push_back(t);
    }
}

mesh.h 文件

#include <iostream>
#include <vector>
#include <set>
#include "stl_reader.h"
#include "vector3D.h"

using namespace std;

struct Triangle{
    vector3D<double> corner[3];
    vector3D<double> normal;
};

struct Vector3DComparator {
    bool operator()(const vector3D<double>& lhs, const vector3D<double>& rhs) const {
        const double TOLERANCE = 0.00000000000001;
        if (fabs(lhs.x - rhs.x) > TOLERANCE) {
            return lhs.x < rhs.x;
        } else if (fabs(lhs.y - rhs.y) > TOLERANCE) {
            return lhs.y < rhs.y;
        } else {
            return lhs.z < rhs.z;
        }
    }
};

class Mesh{
    private:
    vector<Triangle> triangles;
    vector<Triangle> the_chosen_ones;

    bool RayIntersectsTriangle(vector3D<double> rayOrigin, 
                           vector3D<double> rayVector, 
                           Triangle* inTriangle,
                           vector3D<double>& outIntersectionPoint);
    
    void check_bounding_box_collision(vector3D<double> target);

    public:
    unsigned int count_intersections(vector3D<double> target);
    void displace(vector3D<double> distance);
    void load_stl(string filename);
};

我导入的是一个标准20x20x20的立方体STL模型。


内容的提问来源于stack exchange,提问作者dimflix 123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:37:06