JavaFX的8三维场景交点 [英] JavaFX 8 3D scene intersection point

查看:563
本文介绍了JavaFX的8三维场景交点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有可能在一个JavaFX 8三维场景找出沿射线(例如PickRay)点,开始在任意点在三维空间的一些三维方向矢量,其中该射线相交的三角形的网格(TriangleMesh内侧的MeshView)?

Is it possible in a JavaFX 8 3D scene to find points along a ray (e.g. PickRay), starting at an arbitrary point in 3D space with some 3D direction vector, where the ray intersects the triangles in a mesh (TriangleMesh inside a MeshView)?

我知道这是在相机完成/ MouseHandler鼠标采摘,但我看不到任何方式做任意光线。

I know this is done in the Camera/MouseHandler for mouse picking but I can't see any way for doing it for arbitrary rays.

推荐答案

作为@ jdub1581表明,一缕只是一个几何向量,所以要想找到三角形通过这个载体相交的名单,我们需要解决的问题一种线与面和线与面的三角形边界内。

As @jdub1581 suggests, a ray is just a geometric vector, so in order to find a list of triangles intersected by this vector we need to solve problems of the kind 'line intersects plane' and 'line intersects plane within the triangle borders'.

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

Let's assume we have a TriangleMesh, and we have a list of vertices and a list of faces. Each vertex with 3 coordinates, each face with 3 vertices (without considering texture, normals,... ). For the sake of simplicity let's use two lists of Point3D to store them.

在此链接有几个3D形状就可以使用。让我们抓住一个 CuboidMesh

In this link there are several 3D shapes ready to use. Let's grab one CuboidMesh.

CuboidMesh cuboid = new CuboidMesh(10f,12f,4f,4);

这将给我们这个3D形状:

This will give us this 3D shape:

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

Now, if we have a look at the mesh, we could create two lists with vertices and faces:

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));

让在我们的场景添加一些三维点,一个原点和一个目标,无论是在全局坐标,以及定义向量的方向,归一化:

Let's add some 3D points in our scene, one origin and one target, both in global coordinates, and define the direction of the vector, normalized:

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

光线方程,然后将是这样的:

The ray equation then will be this:

r(t) = (4,-7,-4)+t*(-0.154,0.771,0.617)

如果我们加入这两个点之间的细长圆柱体,我们将有我们的光线的视觉重新presentation:

If we add a slender cylinder between these two points we'll have a visual representation of our ray:

包围盒相交

第一步将被检查,如果该射线相交我们的形状的边界框。在外形局部坐标,我们有6个面由它们的法线给出,用自己的6个中心:

The first step will be checking if the ray intersects the bounding box of our shape. In local coordinates of the shape we have 6 faces given by their normals, with their 6 centers:

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()));

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

Since we'll work on the local system, we need our origin point in this coordinates:

Point3D gloOriginInLoc = cuboid.sceneToLocal(gloOrigin); // 4,-7,-4 since the box is centered in 0,0,0

现在,对于任何六个面,我们得到 T 下面这个<一的距离飞机href="http://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm">link.然后,我们可以检查点属于框或没有。

Now, for any of the six faces, we get the distance t to the plane following this link. Then we can check if the point belongs to the box or not.

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()&GT; 0 则有光线和形状之间的一些十字路口,我们可以继续进行三角形。在这个例子中,这些将是交叉点:(3.5,-4.5,-2)和(2.5,0.5,2)

If counter.get()>0 then we have some intersections between the ray and the shape, and we can proceed with the triangles. In this example, these will be the intersection points: (3.5,-4.5, -2) and (2.5,0.5,2).

三角路口

有几种算法发现,如果射线相交网格的任何三角形的任务,所以我们并不需要推倒重来。

There are several algorithms for the task of finding if the ray intersects any triangle of the mesh, so we don't need to reinvent the wheel.

一个我用过的距离托马斯·默勒和放大器;本Trumbore 的。它将提供从原点到平面的距离 T ,坐标 U,V 三角形的内鉴于交集。

The one I've used is from Tomas Möller & Ben Trumbore. It will provide the distance t from the origin to the plane, and the coordinates u,v inside the triangle for a given intersection.

在我们的形状局部坐标系的原点,我们知道了光线的方向,该算法的实现是这样的:

Once we have the origin in local coordinates of the shape, and we know the direction of the ray, the implementation of this algorithm is this:

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年)

In this sample, we find three faces, given by these vertices: (85, 1245, 1274), (85, 1274, 1266) and (351, 1476, 1479).

如果我们绘制这些面会看到路口:

If we plot those faces will see the intersection:

请注意,通过执行所有操作的形状的局部坐标系中,我们节省转化每一三角形的全球系统的操作。

Note that by performing all the operations in the local coordinate system of the shape we save the operations of transforming every triangle to the global system.

这个算法是非常快。我测试过高达3米的三角形,在不到40毫秒。

This algorithm is really fast. I've tested up to 3M triangles in less than 40 ms.

所有code本次测试可用<一个href="https://github.com/Birdasaur/FXyz/blob/master/src/org/fxyz/tests/CuboidTest.java">here.

All the code for this test is available here.

这篇关于JavaFX的8三维场景交点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆