From 9e79333e1e4aadeaca93bf23df56e16b2d8d639f Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Thu, 8 Aug 2024 21:48:43 +0200 Subject: [PATCH] add documentation --- .../eu/jonahbauer/raytracing/math/AABB.java | 30 ++++ .../eu/jonahbauer/raytracing/math/Ray.java | 6 - .../eu/jonahbauer/raytracing/math/Vec3.java | 149 +++++++++++++++--- .../render/renderer/SimpleRenderer.java | 2 +- .../pdf/CosineProbabilityDensityFunction.java | 2 +- .../MixtureProbabilityDensityFunction.java | 7 + .../pdf/ProbabilityDensityFunction.java | 14 ++ .../pdf/SphereProbabilityDensityFunction.java | 3 + .../TargetingProbabilityDensityFunction.java | 1 + .../render/texture/CheckerTexture.java | 1 - 10 files changed, 184 insertions(+), 31 deletions(-) diff --git a/src/main/java/eu/jonahbauer/raytracing/math/AABB.java b/src/main/java/eu/jonahbauer/raytracing/math/AABB.java index cdb8219..76cc556 100644 --- a/src/main/java/eu/jonahbauer/raytracing/math/AABB.java +++ b/src/main/java/eu/jonahbauer/raytracing/math/AABB.java @@ -35,26 +35,49 @@ public record AABB(@NotNull Vec3 min, @NotNull Vec3 max) { return Optional.ofNullable(bbox); } + /** + * {@return the range of x values} + */ public @NotNull Range x() { return new Range(min.x(), max.x()); } + /** + * {@return the range of y values} + */ public @NotNull Range y() { return new Range(min.y(), max.y()); } + /** + * {@return the range of z values} + */ public @NotNull Range z() { return new Range(min.z(), max.z()); } + /** + * {@return the center of this bounding box} + */ public @NotNull Vec3 center() { return Vec3.average(min, max, 2); } + /** + * Expands this bounding box to include the other bounding box. + * @param box a bounding box + * @return the expanded bounding box + */ public @NotNull AABB expand(@NotNull AABB box) { return new AABB(Vec3.min(this.min, box.min), Vec3.max(this.max, box.max)); } + /** + * Tests whether the {@code ray} intersects this bounding box withing the {@code range} + * @param ray a ray + * @param range a range of valid {@code t}s + * @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(); @@ -85,6 +108,13 @@ public record AABB(@NotNull Vec3 min, @NotNull Vec3 max) { return tlmax < tumin && tumin >= range.min() && tlmax <= range.max(); } + /** + * Computes the {@code t} values of the intersections of a ray with the axis-aligned planes through a point. + * @param corner the point + * @param origin the origin point of the ray + * @param invDirection the {@linkplain Vec3#inv() inverted} direction of the ray + * @return a three-element array of the {@code t} values of the intersection with the yz-, xz- and xy-plane through {@code corner} + */ public static double @NotNull[] intersect(@NotNull Vec3 corner, @NotNull Vec3 origin, @NotNull Vec3 invDirection) { return new double[] { (corner.x() - origin.x()) * invDirection.x(), diff --git a/src/main/java/eu/jonahbauer/raytracing/math/Ray.java b/src/main/java/eu/jonahbauer/raytracing/math/Ray.java index c622be1..0f43dc4 100644 --- a/src/main/java/eu/jonahbauer/raytracing/math/Ray.java +++ b/src/main/java/eu/jonahbauer/raytracing/math/Ray.java @@ -13,10 +13,4 @@ public record Ray(@NotNull Vec3 origin, @NotNull Vec3 direction) { public @NotNull Vec3 at(double t) { return Vec3.fma(t, direction, origin); } - - public int vmask() { - return (direction().x() < 0 ? 1 : 0) - | (direction().y() < 0 ? 2 : 0) - | (direction().z() < 0 ? 4 : 0); - } } diff --git a/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java b/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java index 3bdb046..7641756 100644 --- a/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java +++ b/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java @@ -39,6 +39,9 @@ public record Vec3(double x, double y, double z) { return new Vec3(x * factor, y * factor, z * factor); } + /** + * {@return a uniformly random unit vector on the opposite hemisphere of the given direction} + */ public static @NotNull Vec3 randomOppositeHemisphere(@NotNull RandomGenerator random, @NotNull Vec3 direction) { double x, y, z; double squared; @@ -52,11 +55,24 @@ public record Vec3(double x, double y, double z) { return new Vec3(x * factor, y * factor, z * factor); } + /** + * Reflects a vector on the given {@code normal} vector. + * @param vec a vector + * @param normal the surface normal (must be a unit vector) + * @return the reflected vector + */ public static @NotNull Vec3 reflect(@NotNull Vec3 vec, @NotNull Vec3 normal) { var factor = - 2 * normal.times(vec); return Vec3.fma(factor, normal, vec); } + /** + * Refracts a vector on the given {@code normal} vector. + * @param vec a vector + * @param normal the surface normal (must be a unit vector) + * @param ri the refractive index + * @return the refracted vector + */ public static @NotNull Optional refract(@NotNull Vec3 vec, @NotNull Vec3 normal, double ri) { vec = vec.unit(); var cosTheta = Math.min(- vec.times(normal), 1.0); @@ -68,16 +84,35 @@ public record Vec3(double x, double y, double z) { return Optional.of(rOutPerp.plus(rOutParallel)); } + /** + * Rotates a vector around an {@code axis}. + * @param vec a vector + * @param axis the rotation axis + * @param angle the angle in radians + * @return the rotated vector + */ public static @NotNull Vec3 rotate(@NotNull Vec3 vec, @NotNull Vec3 axis, double angle) { Vec3 vxp = axis.cross(vec); Vec3 vxvxp = axis.cross(vxp); return vec.plus(vxp.times(Math.sin(angle))).plus(vxvxp.times(1 - Math.cos(angle))); } + /** + * {@return the euclidean distance between two vectors} + * @param a a vector + * @param b another vector + */ public static double distance(@NotNull Vec3 a, @NotNull Vec3 b) { return a.minus(b).length(); } + /** + * Computes a running average of vectors. + * @param current the current running average + * @param next the next vector + * @param index the one-based index of the next vector + * @return the new running average + */ public static @NotNull Vec3 average(@NotNull Vec3 current, @NotNull Vec3 next, int index) { var factor = 1d / index; return new Vec3( @@ -87,6 +122,11 @@ public record Vec3(double x, double y, double z) { ); } + /** + * {@return a component-wise maximum vector} + * @param a a vector + * @param b another vector + */ public static @NotNull Vec3 max(@NotNull Vec3 a, @NotNull Vec3 b) { return new Vec3( Math.max(a.x(), b.x()), @@ -95,6 +135,11 @@ public record Vec3(double x, double y, double z) { ); } + /** + * {@return a component-wise minimum vector} + * @param a a vector + * @param b another vector + */ public static @NotNull Vec3 min(@NotNull Vec3 a, @NotNull Vec3 b) { return new Vec3( Math.min(a.x(), b.x()), @@ -103,6 +148,12 @@ public record Vec3(double x, double y, double z) { ); } + /** + * {@return a * b + c} + * @param a scalar + * @param b a vector + * @param c another vector + */ public static @NotNull Vec3 fma(double a, @NotNull Vec3 b, @NotNull Vec3 c) { return new Vec3( Math.fma(a, b.x(), c.x()), @@ -119,59 +170,113 @@ public record Vec3(double x, double y, double z) { return new Vec3(this.x - x, this.y - y, this.z - z); } - public @NotNull Vec3 plus(@NotNull Vec3 b) { - return new Vec3(this.x + b.x, this.y + b.y, this.z + b.z); - } - - public @NotNull Vec3 minus(@NotNull Vec3 b) { - return new Vec3(this.x - b.x, this.y - b.y, this.z - b.z); - } - - public double times(@NotNull Vec3 b) { - return this.x * b.x + this.y * b.y + this.z * b.z; - } - - public @NotNull Vec3 times(double b) { - return new Vec3(this.x * b, this.y * b, this.z * b); + /** + * Adds a vector to this vector + * @param other a vector + * @return the sum of this and the other vector + */ + public @NotNull Vec3 plus(@NotNull Vec3 other) { + return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z); } + /** + * Subtracts a vector from this vector + * @param other a vector + * @return the difference of this and the other vector + */ + public @NotNull Vec3 minus(@NotNull Vec3 other) { + return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z); + } + + /** + * Computes the scalar product of this and another vector + * @param other a vector + * @return the scalar product + */ + public double times(@NotNull Vec3 other) { + return this.x * other.x + this.y * other.y + this.z * other.z; + } + + /** + * Multiplies this vector with a scalar + * @param t a scalar + * @return the product of this vector and the scalar + */ + public @NotNull Vec3 times(double t) { + return new Vec3(this.x * t, this.y * t, this.z * t); + } + + /** + * Negates this vector. + * {@return the negated vector} + */ public @NotNull Vec3 neg() { return new Vec3(-x, -y, -z); } + /** + * Inverts each component of this vector. + * @return the inverted vector. + */ public @NotNull Vec3 inv() { return new Vec3(1 / x, 1 / y, 1 / z); } - public @NotNull Vec3 cross(@NotNull Vec3 b) { + /** + * Computes the cross-product of this and another vector + * @param other a vector + * @return the cross-product + */ + public @NotNull Vec3 cross(@NotNull Vec3 other) { return new Vec3( - this.y() * b.z() - b.y() * this.z(), - this.z() * b.x() - b.z() * this.x(), - this.x() * b.y() - b.x() * this.y() + this.y() * other.z() - other.y() * this.z(), + this.z() * other.x() - other.z() * this.x(), + this.x() * other.y() - other.x() * this.y() ); } - public @NotNull Vec3 div(double b) { - return new Vec3(this.x / b, this.y / b, this.z / b); + /** + * Divides this vector by a scalar + * @param t a scalar + * @return this vector divided by the scalar + */ + public @NotNull Vec3 div(double t) { + return new Vec3(this.x / t, this.y / t, this.z / t); } + /** + * {@return the squared length of this vector} + */ public double squared() { return this.x * this.x + this.y * this.y + this.z * this.z; } - + + /** + * {@return the length of this vector} + */ public double length() { return Math.sqrt(squared()); } + /** + * {@return whether this vector is near zero} + */ public boolean isNearZero() { var s = 1e-8; return Math.abs(x) < s && Math.abs(y) < s && Math.abs(z) < s; } - + + /** + * {@return a unit vector with the same direction as this vector} + */ public @NotNull Vec3 unit() { return div(length()); } + /** + * {@return the n-th component of this vector} + * @param axis the component index + */ public double get(int axis) { return switch (axis) { case 0 -> x; diff --git a/src/main/java/eu/jonahbauer/raytracing/render/renderer/SimpleRenderer.java b/src/main/java/eu/jonahbauer/raytracing/render/renderer/SimpleRenderer.java index e7bb9a2..fc29fec 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/renderer/SimpleRenderer.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/renderer/SimpleRenderer.java @@ -179,7 +179,7 @@ public final class SimpleRenderer implements Renderer { } } else { var mixed = new MixtureProbabilityDensityFunction(new TargetingProbabilityDensityFunction(hit.position(), scene.getTargets()), pdf, 0.5); - var direction = mixed.generate(random); + var direction = mixed.generate(random).unit(); var idealPdf = pdf.value(direction); var actualPdf = mixed.value(direction); diff --git a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/CosineProbabilityDensityFunction.java b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/CosineProbabilityDensityFunction.java index d0780e9..6dfe12c 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/CosineProbabilityDensityFunction.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/CosineProbabilityDensityFunction.java @@ -15,7 +15,7 @@ public record CosineProbabilityDensityFunction(@NotNull Vec3 normal) implements @Override public double value(@NotNull Vec3 direction) { - var cos = normal.times(direction.unit()); + var cos = normal.times(direction); return Math.max(0, cos / Math.PI); } 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 273be76..1e896cf 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 @@ -6,6 +6,13 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; import java.util.random.RandomGenerator; +/** + * Mixes between two probability density functions (pdf) using a weight. When the weight is closer to zero, the + * influence of the second pdf is stronger. When the weight is closer to one, the influence of the first pdf is stronger. + * @param a the first probability density function + * @param b the second probability density function + * @param weight a weight in the range [0, 1] + */ public record MixtureProbabilityDensityFunction( @NotNull ProbabilityDensityFunction a, @NotNull ProbabilityDensityFunction b, diff --git a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/ProbabilityDensityFunction.java b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/ProbabilityDensityFunction.java index 3200c1a..7bd87a5 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/ProbabilityDensityFunction.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/ProbabilityDensityFunction.java @@ -5,7 +5,21 @@ import org.jetbrains.annotations.NotNull; import java.util.random.RandomGenerator; +/** + * A probability density function used for sampling random directions when scattering a ray. + */ public interface ProbabilityDensityFunction { + + /** + * {@return the value of this probability density function at the given point} + * @param direction the direction + */ double value(@NotNull Vec3 direction); + + /** + * Generates a random direction based on this probability density function. + * @param random a random number generator + * @return the random direction + */ @NotNull Vec3 generate(@NotNull RandomGenerator random); } diff --git a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/SphereProbabilityDensityFunction.java b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/SphereProbabilityDensityFunction.java index cfe22bb..3f7636d 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/SphereProbabilityDensityFunction.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/renderer/pdf/SphereProbabilityDensityFunction.java @@ -5,6 +5,9 @@ import org.jetbrains.annotations.NotNull; import java.util.random.RandomGenerator; +/** + * A probability density function sampling the sphere uniformly. + */ public record SphereProbabilityDensityFunction() implements ProbabilityDensityFunction { @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 6d95aad..4b2ed62 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 @@ -11,6 +11,7 @@ import java.util.random.RandomGenerator; /** * A probability density function targeting a target. + * @see Target */ public final class TargetingProbabilityDensityFunction implements ProbabilityDensityFunction { private final @NotNull Vec3 origin; diff --git a/src/main/java/eu/jonahbauer/raytracing/render/texture/CheckerTexture.java b/src/main/java/eu/jonahbauer/raytracing/render/texture/CheckerTexture.java index aa282fa..53bc8c6 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/texture/CheckerTexture.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/texture/CheckerTexture.java @@ -5,7 +5,6 @@ import org.jetbrains.annotations.NotNull; public record CheckerTexture(double scale, @NotNull Texture even, @NotNull Texture odd) implements Texture { - @Override public @NotNull Color get(double u, double v, @NotNull Vec3 p) { var x = (int) Math.floor(p.x() / scale);