diff --git a/src/main/java/eu/jonahbauer/raytracing/Main.java b/src/main/java/eu/jonahbauer/raytracing/Main.java index 6050070..93187d8 100644 --- a/src/main/java/eu/jonahbauer/raytracing/Main.java +++ b/src/main/java/eu/jonahbauer/raytracing/Main.java @@ -36,6 +36,7 @@ public class Main { var renderer = SimpleRenderer.builder() .withSamplesPerPixel(500) .withMaxDepth(50) + .withIterative(true) .build(); var image = new LiveCanvas(new Image(camera.getWidth(), camera.getHeight())); 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 36815bb..737b11f 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/renderer/SimpleRenderer.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/renderer/SimpleRenderer.java @@ -17,6 +17,9 @@ public final class SimpleRenderer implements Renderer { private final int maxDepth; private final double gamma; + private final boolean parallel; + private final boolean iterative; + public static @NotNull Builder builder() { return new Builder(); } @@ -29,6 +32,9 @@ public final class SimpleRenderer implements Renderer { this.samplesPerPixel = builder.samplesPerPixel; this.maxDepth = builder.maxDepth; this.gamma = builder.gamma; + + this.parallel = builder.parallel; + this.iterative = builder.iterative; } @Override @@ -37,18 +43,39 @@ public final class SimpleRenderer implements Renderer { throw new IllegalArgumentException("sizes of camera and canvas are different"); } - getPixelStream(camera.getWidth(), camera.getHeight()).parallel().forEach(pixel -> { - var y = (int) (pixel >> 32); - var x = (int) pixel; - - var color = Color.BLACK; - for (int i = 1; i <= samplesPerPixel; i++) { - var ray = camera.cast(x, y); - var c = getColor(scene, ray); - color = Color.average(color, c, i); + if (iterative) { + // render one sample after the other + for (int i = 1 ; i <= samplesPerPixel; i++) { + var sample = i; + getPixelStream(camera.getWidth(), camera.getHeight(), parallel).forEach(pixel -> { + var y = (int) (pixel >> 32); + var x = (int) pixel; + var ray = camera.cast(x, y); + var c = getColor(scene, ray); + canvas.set(x, y, Color.average(canvas.get(x, y), c, sample)); + }); } - canvas.set(x, y, Color.gamma(color, gamma)); - }); + // apply gamma correction + getPixelStream(camera.getWidth(), camera.getHeight(), parallel).forEach(pixel -> { + var y = (int) (pixel >> 32); + var x = (int) pixel; + canvas.set(x, y, Color.gamma(canvas.get(x, y), gamma)); + }); + } else { + // render one pixel after the other + getPixelStream(camera.getWidth(), camera.getHeight(), parallel).forEach(pixel -> { + var y = (int) (pixel >> 32); + var x = (int) pixel; + + var color = Color.BLACK; + for (int i = 1; i <= samplesPerPixel; i++) { + var ray = camera.cast(x, y); + var c = getColor(scene, ray); + color = Color.average(color, c, i); + } + canvas.set(x, y, Color.gamma(color, gamma)); + }); + } } /** @@ -77,10 +104,11 @@ public final class SimpleRenderer implements Renderer { * {@return a stream of the pixels in a canvas with the given size} The pixels {@code x} and {@code y} coordinate * are encoded in the longs lower and upper 32 bits respectively. */ - private static @NotNull LongStream getPixelStream(int width, int height) { - return IntStream.range(0, height) + private static @NotNull LongStream getPixelStream(int width, int height, boolean parallel) { + var stream = IntStream.range(0, height) .mapToObj(y -> IntStream.range(0, width).mapToLong(x -> (long) y << 32 | x)) .flatMapToLong(Function.identity()); + return parallel ? stream.parallel() : stream; } /** @@ -100,6 +128,8 @@ public final class SimpleRenderer implements Renderer { private int samplesPerPixel = 100; private int maxDepth = 10; private double gamma = 2.0; + private boolean parallel = true; + private boolean iterative = false; public @NotNull Builder withSamplesPerPixel(int samples) { if (samples <= 0) throw new IllegalArgumentException("samples must be positive"); @@ -119,6 +149,16 @@ public final class SimpleRenderer implements Renderer { return this; } + public @NotNull Builder withParallel(boolean parallel) { + this.parallel = parallel; + return this; + } + + public @NotNull Builder withIterative(boolean iterative) { + this.iterative = iterative; + return this; + } + public @NotNull SimpleRenderer build() { return new SimpleRenderer(this); }