improve performance by precomputing parts of the AABB intersection algorithm

feature/spectral
jbb01 5 months ago
parent 871c837c34
commit 32b27e2225

@ -79,35 +79,39 @@ public record AABB(@NotNull Vec3 min, @NotNull Vec3 max) {
* @return {@code true} iff the ray intersects this bounding box, {@code false} otherwise
*/
public boolean hit(@NotNull Ray ray, @NotNull Range range) {
var origin = ray.origin();
var direction = ray.direction();
var invDirection = direction.inv();
// calculate t values for intersection points of ray with planes through min
var tmin = intersect(min(), origin, invDirection);
// calculate t values for intersection points of ray with planes through max
var tmax = intersect(max(), origin, invDirection);
// determine range of t for which the ray is inside this voxel
double tlmax = Double.NEGATIVE_INFINITY; // lower limit maximum
double tumin = Double.POSITIVE_INFINITY; // upper limit minimum
for (int i = 0; i < 3; i++) {
// classify t values as lower or upper limit based on ray direction
if (direction.get(i) >= 0) {
// min is lower limit and max is upper limit
if (tmin[i] > tlmax) tlmax = tmin[i];
if (tmax[i] < tumin) tumin = tmax[i];
} else {
// max is lower limit and min is upper limit
if (tmax[i] > tlmax) tlmax = tmax[i];
if (tmin[i] < tumin) tumin = tmin[i];
}
}
var invDirection = ray.getInvDirection();
var negInvOrigin = ray.getNegInvOrigin();
var tminX = Math.fma(min.x(), invDirection.x(), negInvOrigin.x());
var tminY = Math.fma(min.y(), invDirection.y(), negInvOrigin.y());
var tminZ = Math.fma(min.z(), invDirection.z(), negInvOrigin.z());
var tmaxX = Math.fma(max.x(), invDirection.x(), negInvOrigin.x());
var tmaxY = Math.fma(max.y(), invDirection.y(), negInvOrigin.y());
var tmaxZ = Math.fma(max.z(), invDirection.z(), negInvOrigin.z());
var tlmax = max(
Math.min(tminX, tmaxX),
Math.min(tminY, tmaxY),
Math.min(tminZ, tmaxZ)
);
var tumin = min(
Math.max(tminX, tmaxX),
Math.max(tminY, tmaxY),
Math.max(tminZ, tmaxZ)
);
return tlmax < tumin && tumin >= range.min() && tlmax <= range.max();
}
private static double max(double a, double b, double c) {
return Math.max(a, Math.max(b, c));
}
private static double min(double a, double b, double c) {
return Math.min(a, Math.min(b, c));
}
/**
* Computes the {@code t} values of the intersections of a ray with the axis-aligned planes through a point.
* @param corner the point

@ -6,17 +6,35 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public record Ray(@NotNull Vec3 origin, @NotNull Vec3 direction, @NotNull SampledWavelengths lambda) {
public Ray {
Objects.requireNonNull(origin, "origin");
Objects.requireNonNull(direction, "direction");
Objects.requireNonNull(lambda, "lambda");
}
public final class Ray {
private final @NotNull Vec3 origin;
private final @NotNull Vec3 direction;
private final @NotNull SampledWavelengths lambda;
private final @NotNull Vec3 inv;
private final @NotNull Vec3 negInvOrigin;
public Ray(@NotNull Vec3 origin, @NotNull Vec3 direction) {
this(origin, direction, SampledWavelengths.EMPTY);
}
public Ray(@NotNull Vec3 origin, @NotNull Vec3 direction, @NotNull SampledWavelengths lambda) {
this.origin = Objects.requireNonNull(origin, "origin");
this.direction = Objects.requireNonNull(direction, "direction");
this.lambda = Objects.requireNonNull(lambda, "lambda");
this.inv = direction.inv();
this.negInvOrigin = inv.neg().times(origin);
}
private Ray(@NotNull Vec3 origin, @NotNull Vec3 direction, @NotNull SampledWavelengths lambda, @NotNull Vec3 inv, @NotNull Vec3 negInvOrigin) {
this.origin = origin;
this.direction = direction;
this.lambda = lambda;
this.inv = inv;
this.negInvOrigin = negInvOrigin;
}
public @NotNull Vec3 at(double t) {
return Vec3.fma(t, direction, origin);
}
@ -30,6 +48,50 @@ public record Ray(@NotNull Vec3 origin, @NotNull Vec3 direction, @NotNull Sample
}
public @NotNull Ray with(@NotNull SampledWavelengths lambda) {
return new Ray(origin, direction, lambda);
return new Ray(origin, direction, lambda, inv, negInvOrigin);
}
public @NotNull Vec3 origin() {
return origin;
}
public @NotNull Vec3 direction() {
return direction;
}
public @NotNull SampledWavelengths lambda() {
return lambda;
}
public @NotNull Vec3 getInvDirection() {
return inv;
}
public @NotNull Vec3 getNegInvOrigin() {
return negInvOrigin;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (Ray) obj;
return Objects.equals(this.origin, that.origin) &&
Objects.equals(this.direction, that.direction) &&
Objects.equals(this.lambda, that.lambda);
}
@Override
public int hashCode() {
return Objects.hash(origin, direction, lambda);
}
@Override
public @NotNull String toString() {
return "Ray[" +
"origin=" + origin + ", " +
"direction=" + direction + ", " +
"lambda=" + lambda + ']';
}
}

@ -160,6 +160,14 @@ public record Vec3(double x, double y, double z) implements IVec3<Vec3> {
);
}
public static @NotNull Vec3 fma(@NotNull Vec3 a, @NotNull Vec3 b, @NotNull Vec3 c) {
return new Vec3(
Math.fma(a.x(), b.x(), c.x()),
Math.fma(a.y(), b.y(), c.y()),
Math.fma(a.z(), b.z(), c.z())
);
}
public static double tripleProduct(@NotNull Vec3 a, @NotNull Vec3 b, @NotNull Vec3 c) {
return a.x * b.y * c.z + a.y * b.z * c.x + a.z * b.x * c.y - c.x * b.y * a.z - c.y * b.z * a.x - c.z * b.x * a.y;
}

@ -7,10 +7,10 @@ import eu.jonahbauer.raytracing.render.material.Material;
import eu.jonahbauer.raytracing.render.texture.Texture;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.random.RandomGenerator;
/**
* The result of a {@linkplain Hittable#hit(Ray, Range) hit}.
* The result of a {@linkplain Hittable#hit(Ray, Range, RandomGenerator) hit}.
* @param t the {@code t} value at which the hit occurs
* @param position the position of the hit
* @param normal the surface normal at the hit position
@ -24,10 +24,6 @@ public record HitResult(
double t, @NotNull Vec3 position, @NotNull Vec3 normal, @NotNull Hittable target,
@NotNull Material material, double u, double v, boolean isFrontFace
) implements Comparable<HitResult> {
public HitResult {
Objects.requireNonNull(position, "position");
normal = normal.unit();
}
public @NotNull HitResult withPositionAndNormal(@NotNull Vec3 position, @NotNull Vec3 normal) {
return new HitResult(t, position, normal, target, material, u, v, isFrontFace);

Loading…
Cancel
Save