I am writing a raytracer using Java, but I ran into an issue with intersections between rays and triangles. I am using the algorithm given at Scratchapixel, but it is not working properly.
I am testing it using the following code:
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));
The expected output would be 0,0,0
, but instead it is returning null
(no collision).
Full output:
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
Collision method:
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;
}
}
Note: I am using vector.copy().subtract(), etc. because I have made the mathematical operations in-place, so making a copy is necessary for math operations
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();
}
}
The issue was quite simple, I had my cross product implementation wrong, and after that I had to change one line of code.
I changed
double t = (VectorUtils.dotProduct(normal, ray.getPosition()) + d) / angle;
to
double t = (-VectorUtils.dotProduct(normal, ray.getPosition()) + d) / angle;
and it worked.