From 10807112298f76b0408ece707e4a45f07e287cfd Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Sun, 4 Aug 2024 00:18:03 +0200 Subject: [PATCH] add depth of field --- .../java/eu/jonahbauer/raytracing/Main.java | 2 ++ .../jonahbauer/raytracing/render/Camera.java | 31 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/jonahbauer/raytracing/Main.java b/src/main/java/eu/jonahbauer/raytracing/Main.java index dab2326..43be90b 100644 --- a/src/main/java/eu/jonahbauer/raytracing/Main.java +++ b/src/main/java/eu/jonahbauer/raytracing/Main.java @@ -28,6 +28,8 @@ public class Main { .withPosition(new Vec3(-2, 2, 1)) .withTarget(new Vec3(0, 0, -1)) .withFieldOfView(Math.toRadians(20)) + .withFocusDistance(3.4) + .withBlurAngle(Math.toRadians(10)) .build(); var image = camera.render(scene); diff --git a/src/main/java/eu/jonahbauer/raytracing/render/Camera.java b/src/main/java/eu/jonahbauer/raytracing/render/Camera.java index 2a88475..f49c118 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/Camera.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/Camera.java @@ -21,6 +21,7 @@ public final class Camera { private final int samplesPerPixel; private final int maxDepth; private final double gamma; + private final double blurRadius; // internal properties private final @NotNull Vec3 u; @@ -42,7 +43,7 @@ public final class Camera { this.width = builder.imageWidth; this.height = builder.imageHeight; - var viewportHeight = 2 * Math.tan(0.5 * builder.fov); + var viewportHeight = 2 * Math.tan(0.5 * builder.fov) * builder.focusDistance; var viewportWidth = viewportHeight * ((double) width / height); this.origin = builder.position; @@ -51,6 +52,7 @@ public final class Camera { this.samplesPerPixel = builder.samplePerPixel; this.maxDepth = builder.maxDepth; this.gamma = builder.gamma; + this.blurRadius = Math.tan(0.5 * builder.blurAngle) * builder.focusDistance; // project direction the horizontal plane var dXZ = direction.withY(0).unit(); @@ -63,7 +65,7 @@ public final class Camera { this.pixelU = u.times(viewportWidth / width); this.pixelV = v.times(viewportHeight / height); - this.pixel00 = origin.plus(direction) + this.pixel00 = origin.plus(direction.times(builder.focusDistance)) .minus(u.times(0.5 * viewportWidth)).minus(v.times(0.5 * viewportHeight)) .plus(pixelU.div(2)).plus(pixelV.div(2)); } @@ -97,6 +99,17 @@ public final class Camera { } private @NotNull Ray getRay(int x, int y) { + var origin = this.origin; + if (blurRadius > 0) { + double bu, bv; + do { + bu = 2 * Math.random() - 1; + bv = 2 * Math.random() - 1; + } while (bu * bu + bv * bv >= 1); + + origin = origin.plus(u.times(blurRadius * bu)).plus(v.times(blurRadius * bv)); + } + return new Ray(origin, getPixel(x, y).minus(origin)); } @@ -147,6 +160,8 @@ public final class Camera { private double rotation = 0.0; private double fov = 0.5 * Math.PI; + private double focusDistance = 10; + private double blurAngle = 0.0; private int samplePerPixel = 100; private int maxDepth = 10; @@ -190,6 +205,18 @@ public final class Camera { return this; } + public @NotNull Builder withFocusDistance(double focusDistance) { + if (focusDistance <= 0 || !Double.isFinite(focusDistance)) throw new IllegalArgumentException("focus distance must be positive"); + this.focusDistance = focusDistance; + return this; + } + + public @NotNull Builder withBlurAngle(double angle) { + if (angle < 0 || angle >= Math.PI || !Double.isFinite(angle)) throw new IllegalArgumentException("blur-angle must be in the range [0, π)"); + this.blurAngle = angle; + return this; + } + public @NotNull Builder withSamplesPerPixel(int samples) { if (samples <= 0) throw new IllegalArgumentException("samples must be positive"); this.samplePerPixel = samples;