首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >JavaFX 8三维场景交点

JavaFX 8三维场景交点
EN

Stack Overflow用户
提问于 2014-12-09 14:20:01
回答 2查看 2.8K关注 0票数 8

在JavaFX 8三维场景中,是否有可能沿着射线(例如PickRay)找到点,从三维空间中的任意点开始,用某种三维方向向量,其中光线与网格中的三角形(TriangleMesh在MeshView中)相交?

我知道这是在相机/鼠标处理器中做的鼠标选择,但我看不出有任何方法来做任意射线。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-12-23 01:09:57

正如@ just 1581所建议的那样,射线只是一个几何向量,因此为了找到由这个向量相交的三角形列表,我们需要解决‘线相交平面’和‘三角形边界内的直线相交平面’的问题。

假设我们有一个TriangleMesh,我们有一个顶点列表和一个面列表。每个顶点有3个坐标,每个面有3个顶点(不考虑纹理,法线,. )。为了简单起见,让我们使用两个Point3D列表来存储它们。

在这个链接中有几个可以使用的三维形状。我们去拿一个CuboidMesh

代码语言:javascript
运行
复制
CuboidMesh cuboid = new CuboidMesh(10f,12f,4f,4);

这将给我们这个三维形状:

现在,如果我们看一下网格,我们可以创建两个包含顶点和面的列表:

代码语言:javascript
运行
复制
List<Point3D> vertices=Arrays.asList(new Point3D(5.0, 6.0, 2.0), 
        new Point3D(5.0, 6.0, 2.0), new Point3D(5.0, -6.0, 2.0), ..., 
        new Point3D(-1.875, -2.25, -2.0), new Point3D(-1.875, -1.5, -2.0));

List<Point3D> faces=Arrays.asList(new Point3D(0, 386, 388), 
        new Point3D(98, 387, 386.0), new Point3D(100, 388, 387), ..., 
        new Point3D(383, 1535, 1537), new Point3D(1536, 1537, 1535));

让我们在场景中添加一些三维点,一个原点和一个目标,都在全局坐标中,并定义矢量的方向,归一化:

代码语言:javascript
运行
复制
Point3D gloOrigin=new Point3D(4,-7,-4);
Point3D gloTarget=new Point3D(2,3,2);
Point3D direction=gloTarget.subtract(gloOrigin).normalize(); // -0.154,0.771,0.617

射线方程是这样的:

代码语言:javascript
运行
复制
r(t) = (4,-7,-4)+t*(-0.154,0.771,0.617)

如果在这两个点之间加上一个细长的圆柱体,我们就可以直观地表示我们的光线:

边界盒交

第一步是检查光线是否与我们形状的包围盒相交。在这个形状的局部坐标中,我们有6张由法线给出的脸,它们的6个中心:

代码语言:javascript
运行
复制
Bounds locBounds = cuboid.getBoundsInLocal();
List<Point3D> normals=Arrays.asList(new Point3D(-1,0,0),new Point3D(1,0,0),
    new Point3D(0,-1,0), new Point3D(0,1,0), new Point3D(0,0,-1), new Point3D(0,0,1));
List<Point3D> positions=Arrays.asList(new Point3D(locBounds.getMinX(),0,0),
    new Point3D(locBounds.getMaxX(),0,0), new Point3D(0,locBounds.getMinY(),0), 
    new Point3D(0,locBounds.getMaxY(),0), new Point3D(0,0,locBounds.getMinZ()), 
    new Point3D(0,0,locBounds.getMaxZ()));

由于我们将在本地系统上工作,我们需要在这个坐标中的起始点:

代码语言:javascript
运行
复制
Point3D gloOriginInLoc = cuboid.sceneToLocal(gloOrigin); // 4,-7,-4 since the box is centered in 0,0,0

现在,对于这六个面中的任意一个,我们得到t到这个链接后面的飞机的距离。然后我们可以检查这个点是否属于这个盒子。

代码语言:javascript
运行
复制
AtomicInteger counter = new AtomicInteger();
IntStream.range(0, 6).forEach(i->{
    double d=-normals.get(i).dotProduct(positions.get(i));
    double t=-(gloOriginInLoc.dotProduct(normals.get(i))+d)/
              (gloDirection.dotProduct(normals.get(i)));

    Point3D locInter=gloOriginInLoc.add(gloDirection.multiply(t));
    if(locBounds.contains(locInter)){
        counter.getAndIncrement();
    }
});

如果是counter.get()>0,那么在光线和形状之间有一些交点,我们可以继续进行三角形。在这个例子中,这些是交点:(3.5,-4.5,-2)和(2.5,0.5,2)。

三角形相交

有几种算法用于寻找光线是否与网格中的任何三角形相交,所以我们不需要重新发明轮子。

我用过的是来自Tomas M ller和Ben Trumbore的。它将提供从原点到平面的距离t,以及给定的交点在三角形内的坐标u,v

一旦我们得到了形状的局部坐标,并且知道了射线的方向,这个算法的实现如下:

代码语言:javascript
运行
复制
private final float EPS = 0.000001f;

public List<Point3D> getIntersections(Point3D origin, Point3D direction, 
                                      List<Point3D> points, List<Point3D> faces){

    return faces.parallelStream().filter(f->{
        // vertices indices
        int p0=(int)f.getX(); 
        int p1=(int)f.getY(); 
        int p2=(int)f.getZ();

        // vertices 3D coordinates
        Point3D a = points.get(p0);
        Point3D b = points.get(p1);
        Point3D c = points.get(p2);

        Point3D edge1 = b.substract(a);
        Point3D edge2 = c.substract(a);
        Point3D pvec=direction.crossProduct(edge2);
        float det=edge1.dotProduct(pvec);

        if(det<=-EPS || det>=EPS){
            float inv_det=1f/det;
            Point3D tvec=origin.substract(a);
            float u = tvec.dotProduct(pvec)*inv_det;
            if(u>=0f && u<=1f){
                Point3D qvec=tvec.crossProduct(edge1);
                float v = direction.dotProduct(qvec)*inv_det;
                if(v>=0 && u+v<=1f){
                    float t = c.dotProduct(qvec)*inv_det;
                    System.out.println("t: "+t+", u: "+u+", v: "+v);
                    return true;
                }
            }
        }
        return false;
    }).collect(Collectors.toList());
}

在这个样本中,我们找到了由这些顶点给出的三个面:(85,1245,1274),(85,1274,1266)和(351,1476,1479)。

如果我们画出这些面孔,就会看到交叉口:

注意,通过在形状的局部坐标系中执行所有操作,我们节省了将每个三角形转换为全局系统的操作。

这个算法非常快。我在不到40毫秒内测试了3M三角形。

这个测试的所有代码都是可用的这里

票数 11
EN

Stack Overflow用户

发布于 2014-12-20 23:30:45

好吧,我差点杀了这个,所以我会提供一个非常容易理解的教程。它写得很好,必须承认我也学到了很多东西!

我将把数学留给这篇文章,因为它需要讨论的内容很多(转换点和使用矩阵)。

总结:

射线上的任意点是距离原点的函数。

代码语言:javascript
运行
复制
Ray(t) = Origin + Direction(t)

希望这能有所帮助!

编辑:

在Jose的出色示例之后,我冒昧地创建了Ray类,并创建了一个SimpleRayTest示例来显示射线在距离上的路径(将射线看作是投射物)。虽然它不包括三角形交叉口,它应该有助于可视化的射线如何工作。

还可以在Jose提供的库链接中找到资源。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27381119

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档