improve performance by precomputing parts of the AABB intersection algorithm
This commit is contained in:
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
|
* @return {@code true} iff the ray intersects this bounding box, {@code false} otherwise
|
||||||
*/
|
*/
|
||||||
public boolean hit(@NotNull Ray ray, @NotNull Range range) {
|
public boolean hit(@NotNull Ray ray, @NotNull Range range) {
|
||||||
var origin = ray.origin();
|
var invDirection = ray.getInvDirection();
|
||||||
var direction = ray.direction();
|
var negInvOrigin = ray.getNegInvOrigin();
|
||||||
var invDirection = direction.inv();
|
|
||||||
|
|
||||||
// calculate t values for intersection points of ray with planes through min
|
var tminX = Math.fma(min.x(), invDirection.x(), negInvOrigin.x());
|
||||||
var tmin = intersect(min(), origin, invDirection);
|
var tminY = Math.fma(min.y(), invDirection.y(), negInvOrigin.y());
|
||||||
// calculate t values for intersection points of ray with planes through max
|
var tminZ = Math.fma(min.z(), invDirection.z(), negInvOrigin.z());
|
||||||
var tmax = intersect(max(), origin, invDirection);
|
|
||||||
|
|
||||||
// determine range of t for which the ray is inside this voxel
|
var tmaxX = Math.fma(max.x(), invDirection.x(), negInvOrigin.x());
|
||||||
double tlmax = Double.NEGATIVE_INFINITY; // lower limit maximum
|
var tmaxY = Math.fma(max.y(), invDirection.y(), negInvOrigin.y());
|
||||||
double tumin = Double.POSITIVE_INFINITY; // upper limit minimum
|
var tmaxZ = Math.fma(max.z(), invDirection.z(), negInvOrigin.z());
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
var tlmax = max(
|
||||||
// classify t values as lower or upper limit based on ray direction
|
Math.min(tminX, tmaxX),
|
||||||
if (direction.get(i) >= 0) {
|
Math.min(tminY, tmaxY),
|
||||||
// min is lower limit and max is upper limit
|
Math.min(tminZ, tmaxZ)
|
||||||
if (tmin[i] > tlmax) tlmax = tmin[i];
|
);
|
||||||
if (tmax[i] < tumin) tumin = tmax[i];
|
var tumin = min(
|
||||||
} else {
|
Math.max(tminX, tmaxX),
|
||||||
// max is lower limit and min is upper limit
|
Math.max(tminY, tmaxY),
|
||||||
if (tmax[i] > tlmax) tlmax = tmax[i];
|
Math.max(tminZ, tmaxZ)
|
||||||
if (tmin[i] < tumin) tumin = tmin[i];
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tlmax < tumin && tumin >= range.min() && tlmax <= range.max();
|
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.
|
* Computes the {@code t} values of the intersections of a ray with the axis-aligned planes through a point.
|
||||||
* @param corner the point
|
* @param corner the point
|
||||||
|
@ -6,17 +6,35 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public record Ray(@NotNull Vec3 origin, @NotNull Vec3 direction, @NotNull SampledWavelengths lambda) {
|
public final class Ray {
|
||||||
public Ray {
|
private final @NotNull Vec3 origin;
|
||||||
Objects.requireNonNull(origin, "origin");
|
private final @NotNull Vec3 direction;
|
||||||
Objects.requireNonNull(direction, "direction");
|
private final @NotNull SampledWavelengths lambda;
|
||||||
Objects.requireNonNull(lambda, "lambda");
|
|
||||||
}
|
private final @NotNull Vec3 inv;
|
||||||
|
private final @NotNull Vec3 negInvOrigin;
|
||||||
|
|
||||||
public Ray(@NotNull Vec3 origin, @NotNull Vec3 direction) {
|
public Ray(@NotNull Vec3 origin, @NotNull Vec3 direction) {
|
||||||
this(origin, direction, SampledWavelengths.EMPTY);
|
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) {
|
public @NotNull Vec3 at(double t) {
|
||||||
return Vec3.fma(t, direction, origin);
|
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) {
|
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) {
|
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;
|
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 eu.jonahbauer.raytracing.render.texture.Texture;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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 t the {@code t} value at which the hit occurs
|
||||||
* @param position the position of the hit
|
* @param position the position of the hit
|
||||||
* @param normal the surface normal at the hit position
|
* @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,
|
double t, @NotNull Vec3 position, @NotNull Vec3 normal, @NotNull Hittable target,
|
||||||
@NotNull Material material, double u, double v, boolean isFrontFace
|
@NotNull Material material, double u, double v, boolean isFrontFace
|
||||||
) implements Comparable<HitResult> {
|
) implements Comparable<HitResult> {
|
||||||
public HitResult {
|
|
||||||
Objects.requireNonNull(position, "position");
|
|
||||||
normal = normal.unit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull HitResult withPositionAndNormal(@NotNull Vec3 position, @NotNull Vec3 normal) {
|
public @NotNull HitResult withPositionAndNormal(@NotNull Vec3 position, @NotNull Vec3 normal) {
|
||||||
return new HitResult(t, position, normal, target, material, u, v, isFrontFace);
|
return new HitResult(t, position, normal, target, material, u, v, isFrontFace);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user