光线三角形相交算法不起作用
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;
成功了。
我正在使用 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;
成功了。