From dbd3d5fc4b6ccd4f87273508b56cd4485114d348 Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:16:11 +0200 Subject: [PATCH] minor performance improvements --- .../MixtureProbabilityDensityFunction.java | 4 +- .../TargetingProbabilityDensityFunction.java | 12 +++-- .../jonahbauer/raytracing/scene/Hittable.java | 3 +- .../scene/hittable2d/Hittable2D.java | 17 +++++++ .../scene/hittable2d/Parallelogram.java | 15 +++---- .../raytracing/scene/hittable3d/Box.java | 10 ++--- .../raytracing/scene/hittable3d/Sphere.java | 45 ++++++++++++------- .../raytracing/scene/util/PdfUtil.java | 12 ++--- 8 files changed, 77 insertions(+), 41 deletions(-) diff --git a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/MixtureProbabilityDensityFunction.java b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/MixtureProbabilityDensityFunction.java index d45a915..273be76 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/MixtureProbabilityDensityFunction.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/MixtureProbabilityDensityFunction.java @@ -23,7 +23,9 @@ public record MixtureProbabilityDensityFunction( @Override public double value(@NotNull Vec3 direction) { - return weight * a.value(direction) + (1 - weight) * b.value(direction); + var v = a.value(direction); + var w = b.value(direction); + return Math.fma(weight, v, Math.fma(-weight, w, w)); } @Override diff --git a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/TargetingProbabilityDensityFunction.java b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/TargetingProbabilityDensityFunction.java index d63f51b..6d95aad 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/TargetingProbabilityDensityFunction.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/TargetingProbabilityDensityFunction.java @@ -4,6 +4,7 @@ import eu.jonahbauer.raytracing.math.Vec3; import eu.jonahbauer.raytracing.scene.Target; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.random.RandomGenerator; @@ -11,10 +12,13 @@ import java.util.random.RandomGenerator; /** * A probability density function targeting a target. */ -public record TargetingProbabilityDensityFunction(@NotNull Vec3 origin, @NotNull List<@NotNull Target> targets) implements ProbabilityDensityFunction { - public TargetingProbabilityDensityFunction { - Objects.requireNonNull(origin, "origin"); - targets = List.copyOf(targets); +public final class TargetingProbabilityDensityFunction implements ProbabilityDensityFunction { + private final @NotNull Vec3 origin; + private final @NotNull List<@NotNull Target> targets; + + public TargetingProbabilityDensityFunction(@NotNull Vec3 origin, @NotNull List<@NotNull Target> targets) { + this.origin = Objects.requireNonNull(origin, "origin"); + this.targets = new ArrayList<>(targets); } @Override diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java b/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java index 6df3617..f78c75c 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java @@ -11,12 +11,13 @@ import org.jetbrains.annotations.NotNull; import java.util.Optional; public interface Hittable { + @NotNull Range FORWARD = new Range(0.001, Double.POSITIVE_INFINITY); /** * @see #hit(Ray, Range) */ default @NotNull Optional hit(@NotNull Ray ray) { - return hit(ray, new Range(0.001, Double.POSITIVE_INFINITY)); + return hit(ray, FORWARD); } /** diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Hittable2D.java b/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Hittable2D.java index 23282b6..9af3860 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Hittable2D.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Hittable2D.java @@ -57,6 +57,23 @@ public abstract class Hittable2D implements Hittable { )); } + protected double hit0(@NotNull Ray ray, @NotNull Range range) { + var denominator = ray.direction().times(normal); + if (Math.abs(denominator) < 1e-8) return Double.NaN; // parallel + + var t = (d - ray.origin().times(normal)) / denominator; + if (!range.surrounds(t)) return Double.NaN; + + var position = ray.at(t); + var p = position.minus(origin); + + var alpha = w.times(p.cross(v)); + var beta = w.times(u.cross(p)); + if (!isInterior(alpha, beta)) return Double.NaN; + + return t; + } + protected abstract boolean isInterior(double alpha, double beta); @Override diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Parallelogram.java b/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Parallelogram.java index e4cfbd8..da77727 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Parallelogram.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/hittable2d/Parallelogram.java @@ -31,14 +31,13 @@ public final class Parallelogram extends Hittable2D implements Target { @Override public double getProbabilityDensity(@NotNull Vec3 origin, @NotNull Vec3 direction) { - var result = hit(new Ray(origin, direction)); - if (result.isEmpty()) return 0; - - var a = this.origin; - var b = this.origin.plus(u); - var c = this.origin.plus(v); - var d = b.plus(v); - var angle = PdfUtil.getSolidAngle(origin, a, b, d) + PdfUtil.getSolidAngle(origin, c, b, d); + if (Double.isNaN(hit0(new Ray(origin, direction), FORWARD))) return 0; + + var a = this.origin.minus(origin).unit(); + var b = this.origin.plus(u).minus(origin).unit(); + var c = this.origin.plus(v).minus(origin).unit(); + var d = this.origin.plus(u).plus(v).minus(origin).unit(); + var angle = PdfUtil.getSolidAngle(a, b, d) + PdfUtil.getSolidAngle(c, b, d); return 1 / angle; } diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Box.java b/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Box.java index 123f09a..c30c603 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Box.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Box.java @@ -293,12 +293,12 @@ public final class Box implements Hittable, Target { * {@return the solid angle covered by this side of the box when viewed from pos} */ public double getSolidAngle(@NotNull AABB box, @NotNull Vec3 pos) { - var a = get(box, 0, 0); - var b = get(box, 0, 1); - var c = get(box, 1, 1); - var d = get(box, 1, 0); + var a = get(box, 0, 0).minus(pos).unit(); + var b = get(box, 0, 1).minus(pos).unit(); + var c = get(box, 1, 1).minus(pos).unit(); + var d = get(box, 1, 0).minus(pos).unit(); - return PdfUtil.getSolidAngle(pos, a, b, d) + PdfUtil.getSolidAngle(pos, c, b, d); + return PdfUtil.getSolidAngle(a, b, d) + PdfUtil.getSolidAngle(c, b, d); } } } diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Sphere.java b/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Sphere.java index f2074b4..e10da56 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Sphere.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/hittable3d/Sphere.java @@ -21,12 +21,18 @@ public final class Sphere implements Hittable, Target { private final @NotNull AABB bbox; + private final @NotNull Vec3 normalizedCenter; + private final double invRadius; + public Sphere(@NotNull Vec3 center, double radius, @NotNull Material material) { this.center = Objects.requireNonNull(center, "center"); this.material = Objects.requireNonNull(material, "material"); if (radius <= 0 || !Double.isFinite(radius)) throw new IllegalArgumentException("radius must be positive"); this.radius = radius; + this.invRadius = 1 / radius; + this.normalizedCenter = this.center.times(-this.invRadius); + this.bbox = new AABB( center.minus(radius, radius, radius), center.plus(radius, radius, radius) @@ -35,23 +41,11 @@ public final class Sphere implements Hittable, Target { @Override public @NotNull Optional hit(@NotNull Ray ray, @NotNull Range range) { - var oc = ray.origin().minus(center); - - var a = ray.direction().squared(); - var h = ray.direction().times(oc); - var c = oc.squared() - radius * radius; - - var discriminant = h * h - a * c; - if (discriminant < 0) return Optional.empty(); - - var sd = Math.sqrt(discriminant); - - double t = (- h - sd) / a; - if (!range.surrounds(t)) t = (- h + sd) / a; - if (!range.surrounds(t)) return Optional.empty(); + var t = hit0(ray, range); + if (Double.isNaN(t)) return Optional.empty(); var position = ray.at(t); - var normal = position.minus(center).div(radius); + var normal = Vec3.fma(invRadius, position, normalizedCenter); var frontFace = normal.times(ray.direction()) < 0; double u; @@ -72,6 +66,25 @@ public final class Sphere implements Hittable, Target { )); } + private double hit0(@NotNull Ray ray, @NotNull Range range) { + var oc = ray.origin().minus(center); + + var a = ray.direction().squared(); + var h = ray.direction().times(oc); + var c = oc.squared() - radius * radius; + + var discriminant = h * h - a * c; + if (discriminant < 0) return Double.NaN; + + var sd = Math.sqrt(discriminant); + + double t = (- h - sd) / a; + if (!range.surrounds(t)) t = (- h + sd) / a; + if (!range.surrounds(t)) return Double.NaN; + + return t; + } + @Override public @NotNull AABB getBoundingBox() { return bbox; @@ -79,7 +92,7 @@ public final class Sphere implements Hittable, Target { @Override public double getProbabilityDensity(@NotNull Vec3 origin, @NotNull Vec3 direction) { - if (hit(new Ray(origin, direction)).isEmpty()) return 0; + if (Double.isNaN(hit0(new Ray(origin, direction), FORWARD))) return 0; var cos = Math.sqrt(1 - radius * radius / (center.minus(origin).squared())); var solidAngle = 2 * Math.PI * (1 - cos); diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/util/PdfUtil.java b/src/main/java/eu/jonahbauer/raytracing/scene/util/PdfUtil.java index 09c0d4f..8f7b70f 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/util/PdfUtil.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/util/PdfUtil.java @@ -8,12 +8,12 @@ public final class PdfUtil { throw new UnsupportedOperationException(); } - public static double getSolidAngle(@NotNull Vec3 o, @NotNull Vec3 a, @NotNull Vec3 b, @NotNull Vec3 c) { - var i = a.minus(o).unit(); - var j = b.minus(o).unit(); - var k = c.minus(o).unit(); - - var angle = 2 * Math.atan(Math.abs(i.times(j.cross(k))) / (1 + i.times(j) + j.times(k) + k.times(i))); + /** + * {@return the solid angle of the triangle abc as seen from the origin} The vectors {@code a}, {@code b} and {@code c} + * must be unit vectors. + */ + public static double getSolidAngle(@NotNull Vec3 a, @NotNull Vec3 b, @NotNull Vec3 c) { + var angle = 2 * Math.atan(Math.abs(a.times(b.cross(c))) / (1 + a.times(b) + b.times(c) + c.times(a))); return angle < 0 ? 2 * Math.PI + angle : angle; } }