From af3dc8dac7a2fb09437ed2075d6f9645e8efb5f5 Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Sat, 3 Aug 2024 02:29:16 +0200 Subject: [PATCH] refactor Camera --- .../jonahbauer/raytracing/render/Camera.java | 164 ++++++++++++------ .../eu/jonahbauer/raytracing/scene/Scene.java | 10 -- 2 files changed, 111 insertions(+), 63 deletions(-) diff --git a/src/main/java/eu/jonahbauer/raytracing/render/Camera.java b/src/main/java/eu/jonahbauer/raytracing/render/Camera.java index 1a60890..8098af8 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/Camera.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/Camera.java @@ -7,22 +7,24 @@ import eu.jonahbauer.raytracing.scene.Scene; import org.jetbrains.annotations.NotNull; import java.util.Objects; -import java.util.stream.IntStream; -import java.util.stream.Stream; -public record Camera( - int width, int height, - double viewportWidth, double viewportHeight, - @NotNull Vec3 origin, @NotNull Vec3 direction -) { - public Camera { - if (width <= 0) throw new IllegalArgumentException("width must be positive"); - if (height <= 0) throw new IllegalArgumentException("height must be positive"); - if (viewportWidth <= 0 || !Double.isFinite(viewportWidth)) throw new IllegalArgumentException("viewportWidth must be positive"); - if (viewportHeight <= 0 || !Double.isFinite(viewportHeight)) throw new IllegalArgumentException("viewportHeight must be positive"); - Objects.requireNonNull(origin, "origin"); - Objects.requireNonNull(direction, "direction"); - } +public final class Camera { + // image size + private final int width; + private final int height; + + // viewport size + private final double viewportWidth; + private final double viewportHeight; + + // camera position and orientation + private final @NotNull Vec3 origin; + private final @NotNull Vec3 direction; + + // internal properties + private final @NotNull Vec3 pixelU; + private final @NotNull Vec3 pixelV; + private final @NotNull Vec3 pixel00; /** * Creates a new camera with the given dimensions at the origin facing towards positive z with a focal length of 1. @@ -45,35 +47,23 @@ public record Camera( this(width, height, viewportWidth, viewportHeight, Vec3.ZERO, Vec3.UNIT_Z); } - public @NotNull Image render(@NotNull Scene scene) { - var image = new Image(width(), height()); + public Camera( + int width, int height, + double viewportWidth, double viewportHeight, + @NotNull Vec3 origin, @NotNull Vec3 direction + ) { + if (width <= 0) throw new IllegalArgumentException("width must be positive"); + if (height <= 0) throw new IllegalArgumentException("height must be positive"); + if (viewportWidth <= 0 || !Double.isFinite(viewportWidth)) throw new IllegalArgumentException("viewportWidth must be positive"); + if (viewportHeight <= 0 || !Double.isFinite(viewportHeight)) throw new IllegalArgumentException("viewportHeight must be positive"); - pixels().forEach(pixel -> { - var x = pixel.x(); - var y = pixel.y(); - var ray = pixel.ray(); + this.width = width; + this.height = height; + this.viewportWidth = viewportWidth; + this.viewportHeight = viewportHeight; + this.origin = Objects.requireNonNull(origin, "origin"); + this.direction = Objects.requireNonNull(direction, "direction"); - var result = scene.hit(ray, Range.NON_NEGATIVE); - if (result.isPresent()) { - var normal = result.get().normal(); - image.set(x, y, getNormalColor(normal)); - } else { - image.set(x, y, scene.getSkyboxColor(ray)); - } - }); - - return image; - } - - private @NotNull Color getNormalColor(@NotNull Vec3 normal) { - return new Color( - 0.5 * (normal.x() + 1), - 0.5 * (normal.y() + 1), - 0.5 * (normal.z() + 1) - ); - } - - public @NotNull Stream pixels() { // project direction onto xz-plane var d = direction.unit(); var dXZ = direction.withY(0).unit(); @@ -84,20 +74,88 @@ public record Camera( viewportU = viewportU.times(viewportWidth); // vector along the width of the viewport viewportV = viewportV.times(- viewportHeight); // vector along the height of the viewport - var pixelU = viewportU.div(width); - var pixelV = viewportV.div(height); + this.pixelU = viewportU.div(width); + this.pixelV = viewportV.div(height); - var pixel00 = origin.plus(direction) + this.pixel00 = origin.plus(direction) .minus(viewportU.div(2)).minus(viewportV.div(2)) .plus(pixelU.div(2)).plus(pixelV.div(2)); - - return IntStream.range(0, width * height).mapToObj(i -> { - var y = i / width; - var x = i % width; - var pixel = pixel00.plus(pixelU.times(x)).plus(pixelV.times(y)); - return new Pixel(x, y, new Ray(origin, pixel.minus(origin))); - }); } - public record Pixel(int x, int y, @NotNull Ray ray) {} + public @NotNull Image render(@NotNull Scene scene) { + var image = new Image(width, height); + + for (int y = 0; y < height; y++) { + var ray = getRay(x, y); + var color = getColor(scene, ray); + image.set(x, y, color); + for (int x = 0; x < width; x++) { + } + } + + return image; + } + + private @NotNull Ray getRay(int x, int y) { + return new Ray(origin, getPixel(x, y).minus(origin)); + } + + private @NotNull Vec3 getPixel(int x, int y) { + Objects.checkIndex(x, width); + Objects.checkIndex(y, height); + return pixel00.plus(pixelU.times(x)).plus(pixelV.times(y)); + } + + private @NotNull Color getColor(@NotNull Scene scene, @NotNull Ray ray) { + var result = scene.hit(ray, Range.NON_NEGATIVE); + if (result.isPresent()) { + var normal = result.get().normal(); + return getNormalColor(normal); + } else { + return getSkyboxColor(ray); + } + } + + private static @NotNull Color getNormalColor(@NotNull Vec3 normal) { + return new Color( + 0.5 * (normal.x() + 1), + 0.5 * (normal.y() + 1), + 0.5 * (normal.z() + 1) + ); + } + + private static @NotNull Color getSkyboxColor(@NotNull Ray ray) { + // altitude from -pi/2 to pi/2 + var alt = Math.copySign( + Math.acos(ray.direction().withY(0).unit().times(ray.direction().unit())), + ray.direction().y() + ); + return Color.lerp(Color.WHITE, Color.SKY, alt / Math.PI + 0.5); + } + + // getters + + public int width() { + return width; + } + + public int height() { + return height; + } + + public double viewportWidth() { + return viewportWidth; + } + + public double viewportHeight() { + return viewportHeight; + } + + public @NotNull Vec3 origin() { + return origin; + } + + public @NotNull Vec3 direction() { + return direction; + } } diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/Scene.java b/src/main/java/eu/jonahbauer/raytracing/scene/Scene.java index ffb475c..7430895 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/Scene.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/Scene.java @@ -2,7 +2,6 @@ package eu.jonahbauer.raytracing.scene; import eu.jonahbauer.raytracing.math.Range; import eu.jonahbauer.raytracing.math.Ray; -import eu.jonahbauer.raytracing.render.Color; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -29,13 +28,4 @@ public record Scene(@NotNull List<@NotNull Hittable> objects) implements Hittabl } return Optional.ofNullable(result); } - - public @NotNull Color getSkyboxColor(@NotNull Ray ray) { - // altitude from -pi/2 to pi/2 - var alt = Math.copySign( - Math.acos(ray.direction().withY(0).unit().times(ray.direction().unit())), - ray.direction().y() - ); - return Color.lerp(Color.WHITE, Color.SKY, alt / Math.PI + 0.5); - } }