From 14fd1d73fc8b16fad4069ffbefe4f55806ed6a6a Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Sat, 3 Aug 2024 01:44:27 +0200 Subject: [PATCH] add shading according to normal vector --- .../eu/jonahbauer/raytracing/render/Scene.java | 16 ++++++++++++---- .../jonahbauer/raytracing/shape/HitResult.java | 16 ++++++++++++++++ .../eu/jonahbauer/raytracing/shape/Shape.java | 4 +++- .../eu/jonahbauer/raytracing/shape/Sphere.java | 9 +++++---- .../jonahbauer/raytracing/shape/SphereTest.java | 6 +++--- 5 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 src/main/java/eu/jonahbauer/raytracing/shape/HitResult.java diff --git a/src/main/java/eu/jonahbauer/raytracing/render/Scene.java b/src/main/java/eu/jonahbauer/raytracing/render/Scene.java index 02f4dba..aed625f 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/Scene.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/Scene.java @@ -4,7 +4,9 @@ import eu.jonahbauer.raytracing.math.Ray; import eu.jonahbauer.raytracing.shape.Shape; import org.jetbrains.annotations.NotNull; +import java.util.Comparator; import java.util.List; +import java.util.Optional; public record Scene(@NotNull List<@NotNull Shape> shapes) { @@ -25,11 +27,17 @@ public record Scene(@NotNull List<@NotNull Shape> shapes) { var ray = pixel.ray(); var result = shapes.stream() - .mapToDouble(shape -> shape.hit(ray)) - .filter(Double::isFinite) - .min(); + .map(shape -> shape.hit(ray)) + .flatMap(Optional::stream) + .min(Comparator.naturalOrder()); + if (result.isPresent()) { - image.set(x, y, Color.RED); + var normal = result.get().normal(); + image.set(x, y, new Color( + 0.5 * (normal.x() + 1), + 0.5 * (normal.y() + 1), + 0.5 * (normal.z() + 1) + )); } else { image.set(x, y, getSkyboxColor(ray)); } diff --git a/src/main/java/eu/jonahbauer/raytracing/shape/HitResult.java b/src/main/java/eu/jonahbauer/raytracing/shape/HitResult.java new file mode 100644 index 0000000..b5c519f --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/shape/HitResult.java @@ -0,0 +1,16 @@ +package eu.jonahbauer.raytracing.shape; + +import eu.jonahbauer.raytracing.math.Vec3; +import org.jetbrains.annotations.NotNull; + +public record HitResult(double t, @NotNull Vec3 normal) implements Comparable { + public HitResult { + if (t < 0 || !Double.isFinite(t)) throw new IllegalArgumentException("t must be non-negative"); + normal = normal.unit(); + } + + @Override + public int compareTo(@NotNull HitResult o) { + return Double.compare(t, o.t); + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/shape/Shape.java b/src/main/java/eu/jonahbauer/raytracing/shape/Shape.java index ef3fe77..a6b2e57 100644 --- a/src/main/java/eu/jonahbauer/raytracing/shape/Shape.java +++ b/src/main/java/eu/jonahbauer/raytracing/shape/Shape.java @@ -3,6 +3,8 @@ package eu.jonahbauer.raytracing.shape; import eu.jonahbauer.raytracing.math.Ray; import org.jetbrains.annotations.NotNull; +import java.util.Optional; + public sealed interface Shape permits Sphere { /** @@ -10,5 +12,5 @@ public sealed interface Shape permits Sphere { * the ray origin, or Double.NaN if the ray does not intersect this shape} * @param ray a ray */ - double hit(@NotNull Ray ray); + @NotNull Optional hit(@NotNull Ray ray); } diff --git a/src/main/java/eu/jonahbauer/raytracing/shape/Sphere.java b/src/main/java/eu/jonahbauer/raytracing/shape/Sphere.java index 020de3f..d08e346 100644 --- a/src/main/java/eu/jonahbauer/raytracing/shape/Sphere.java +++ b/src/main/java/eu/jonahbauer/raytracing/shape/Sphere.java @@ -5,6 +5,7 @@ import eu.jonahbauer.raytracing.math.Vec3; import org.jetbrains.annotations.NotNull; import java.util.Objects; +import java.util.Optional; public record Sphere(@NotNull Vec3 center, double radius) implements Shape { public static final @NotNull Sphere UNIT = new Sphere(Vec3.ZERO, 1.0); @@ -19,7 +20,7 @@ public record Sphere(@NotNull Vec3 center, double radius) implements Shape { } @Override - public double hit(@NotNull Ray ray) { + public @NotNull Optional hit(@NotNull Ray ray) { var oc = ray.origin().minus(center()); var a = ray.direction().squared(); @@ -27,14 +28,14 @@ public record Sphere(@NotNull Vec3 center, double radius) implements Shape { var c = oc.squared() - radius * radius; var discriminant = b * b - 4 * a * c; - if (discriminant < 0) return Double.NaN; + if (discriminant < 0) return Optional.empty(); var sd = Math.sqrt(discriminant); double t = (- b - sd) / (2 * a); if (t < 0) t = (-b + sd) / (2 * a); - if (t < 0) t = Double.NaN; - return t; + if (t < 0) return Optional.empty(); + return Optional.of(new HitResult(t, ray.at(t).minus(center))); } public @NotNull Sphere withCenter(@NotNull Vec3 center) { diff --git a/src/test/java/eu/jonahbauer/raytracing/shape/SphereTest.java b/src/test/java/eu/jonahbauer/raytracing/shape/SphereTest.java index 5bec826..6fe83f5 100644 --- a/src/test/java/eu/jonahbauer/raytracing/shape/SphereTest.java +++ b/src/test/java/eu/jonahbauer/raytracing/shape/SphereTest.java @@ -18,8 +18,8 @@ class SphereTest { var direction = new Vec3(-1, -1, -1); var ray = new Ray(origin, direction); - var t = sphere.hit(ray); - assertFalse(Double.isNaN(t)); - assertEquals(center.plus(new Vec3(1, 1, 1).unit().times(radius)), ray.at(t)); + var result = sphere.hit(ray); + assertFalse(result.isEmpty()); + assertEquals(center.plus(new Vec3(1, 1, 1).unit().times(radius)), ray.at(result.get().t())); } } \ No newline at end of file