光线三角形相交算法不起作用

Ray-triangle intersection algorithm not working

我正在使用 Java 编写光线追踪器,但我 运行 遇到了光线与三角形相交的问题。我正在使用 Scratchapixel 中给出的算法,但它无法正常工作。

我正在使用以下代码对其进行测试:

Face3D triangle = new Face3D(
        new Vector3D(-1, -1, 0),
        new Vector3D(1, -1, 0),
        new Vector3D(0, 1, 0)
);
Ray3D ray = new Ray3D(
        new Vector3D(0, 0, -1),
        new Vector3D(0, 0, 1)
);
log.info(triangle.getNormal());
log.info(triangle.collision(ray));

预期输出为 0,0,0,但它返回 null(无冲突)。

完整输出:

17:31:56.013 [main] INFO  org.jrender.Main - Vector3D{x=1.0, y=0.0, z=0.0}
17:31:56.017 [main] INFO  org.jrender.space.Face3D - angle: 0.0
17:31:56.018 [main] INFO  org.jrender.Main - null

碰撞方式:

public class Face3D {
        // ... getters/setters etc.
        public Vector3D collision(Ray3D ray) {
        if (vertices.size() != 3)
            throw new UnsupportedOperationException("Normal can only be calculated on triangles");

        double angle = VectorUtils.dotProduct(normal, ray.getDirection());
        log.info("angle: " + angle);
        if (Math.abs(angle) < Constants.EPSILON) return null; // Constants.EPSILON = 0.001

        double d = VectorUtils.dotProduct(normal, v0);
        double t = (VectorUtils.dotProduct(normal, ray.getPosition()) + d) / angle;
        log.info("d: " + d + "; t: " + t);
        if (t < 0) return null;

        Vector3D intersection = ray.getPosition().copy().add(ray.getDirection().copy().multiply(t));
        log.info("intersection: " + intersection);
        Vector3D perpendicular;
        Vector3D edge;
        Vector3D distIntersection;

        edge = v1.copy().subtract(v0);
        distIntersection = intersection.copy().subtract(v0);
        perpendicular = VectorUtils.crossProduct(edge, distIntersection);
        if (VectorUtils.dotProduct(normal, perpendicular) < 0) return null;

        edge = v2.copy().subtract(v1);
        distIntersection = intersection.copy().subtract(v1);
        perpendicular = VectorUtils.crossProduct(edge, distIntersection);
        if (VectorUtils.dotProduct(normal, perpendicular) < 0) return null;

        edge = v0.copy().subtract(v2);
        distIntersection = intersection.copy().subtract(v2);
        perpendicular = VectorUtils.crossProduct(edge, distIntersection);
        if (VectorUtils.dotProduct(normal, perpendicular) < 0) return null;

        return intersection;
    }
}

注意:我使用的是vector.copy().subtract()等,因为我已经就地进行了数学运算,所以数学运算需要复制一份

我用过的实用方法

public class Vector3D {
    // ... getters/setters, etc.
    public Vector3D normalize() {
        return divide(Math.sqrt(x * x + y * y + z * z));
    }

    public Vector3D add(Vector3D v) {
        x += v.x;
        y += v.y;
        z += v.z;
        return this;
    }

    public Vector3D subtract(Vector3D v) {
        x -= v.x;
        y -= v.y;
        z -= v.z;
        return this;
    }

    public Vector3D multiply(double fac) {
        x *= fac;
        y *= fac;
        z *= fac;
        return this;
    }

    public Vector3D divide(double fac) {
        x /= fac;
        y /= fac;
        z /= fac;
        return this;
    }

    public Vector3D copy() {
        return new Vector3D(x, y, z);
    }
}
public class VectorUtils {
    public static Vector3D crossProduct(Vector3D first, Vector3D second) {
        return new Vector3D(
                first.getY() * second.getZ() - first.getZ() * second.getY(),
                first.getZ() * second.getX() - first.getX() * second.getZ(),
                first.getX() * second.getY() - first.getY() * second.getX()
        );
    }

    public static double dotProduct(Vector3D first, Vector3D second) {
        return first.getX() * second.getX() +
                first.getY() * second.getY() +
                first.getZ() * second.getZ();
    }
}

问题很简单,我的叉积实现有误,改了一行代码

我变了

double t = (VectorUtils.dotProduct(normal, ray.getPosition()) + d) / angle;

double t = (-VectorUtils.dotProduct(normal, ray.getPosition()) + d) / angle;

成功了。