add documentation

This commit is contained in:
jbb01 2024-08-08 21:48:43 +02:00
parent dbd3d5fc4b
commit 9e79333e1e
10 changed files with 184 additions and 31 deletions

View File

@ -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(),

View File

@ -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);
}
}

View File

@ -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 <code>direction</code>}
*/
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<Vec3> 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 <code>a * b + c</code>}
* @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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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);