add iterative rendering mode for faster results
In normal mode, the image is rendered one pixel at a time, taking multiple samples per pixel and averaging them before continuing with the next pixel. In iterative mode, the image is rendered one sample at a time, taking one sample per pixel, then taking another sample and averaging it with the one before, and so on.
This commit is contained in:
parent
0c6db707e0
commit
b47ded6c56
@ -36,6 +36,7 @@ public class Main {
|
|||||||
var renderer = SimpleRenderer.builder()
|
var renderer = SimpleRenderer.builder()
|
||||||
.withSamplesPerPixel(500)
|
.withSamplesPerPixel(500)
|
||||||
.withMaxDepth(50)
|
.withMaxDepth(50)
|
||||||
|
.withIterative(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
var image = new LiveCanvas(new Image(camera.getWidth(), camera.getHeight()));
|
var image = new LiveCanvas(new Image(camera.getWidth(), camera.getHeight()));
|
||||||
|
@ -17,6 +17,9 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
private final int maxDepth;
|
private final int maxDepth;
|
||||||
private final double gamma;
|
private final double gamma;
|
||||||
|
|
||||||
|
private final boolean parallel;
|
||||||
|
private final boolean iterative;
|
||||||
|
|
||||||
public static @NotNull Builder builder() {
|
public static @NotNull Builder builder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
@ -29,6 +32,9 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
this.samplesPerPixel = builder.samplesPerPixel;
|
this.samplesPerPixel = builder.samplesPerPixel;
|
||||||
this.maxDepth = builder.maxDepth;
|
this.maxDepth = builder.maxDepth;
|
||||||
this.gamma = builder.gamma;
|
this.gamma = builder.gamma;
|
||||||
|
|
||||||
|
this.parallel = builder.parallel;
|
||||||
|
this.iterative = builder.iterative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -37,7 +43,27 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
throw new IllegalArgumentException("sizes of camera and canvas are different");
|
throw new IllegalArgumentException("sizes of camera and canvas are different");
|
||||||
}
|
}
|
||||||
|
|
||||||
getPixelStream(camera.getWidth(), camera.getHeight()).parallel().forEach(pixel -> {
|
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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 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 y = (int) (pixel >> 32);
|
||||||
var x = (int) pixel;
|
var x = (int) pixel;
|
||||||
|
|
||||||
@ -50,6 +76,7 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
canvas.set(x, y, Color.gamma(color, gamma));
|
canvas.set(x, y, Color.gamma(color, gamma));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@return the color of the given ray in the given scene}
|
* {@return the color of the given ray in the given scene}
|
||||||
@ -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
|
* {@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.
|
* are encoded in the longs lower and upper 32 bits respectively.
|
||||||
*/
|
*/
|
||||||
private static @NotNull LongStream getPixelStream(int width, int height) {
|
private static @NotNull LongStream getPixelStream(int width, int height, boolean parallel) {
|
||||||
return IntStream.range(0, height)
|
var stream = IntStream.range(0, height)
|
||||||
.mapToObj(y -> IntStream.range(0, width).mapToLong(x -> (long) y << 32 | x))
|
.mapToObj(y -> IntStream.range(0, width).mapToLong(x -> (long) y << 32 | x))
|
||||||
.flatMapToLong(Function.identity());
|
.flatMapToLong(Function.identity());
|
||||||
|
return parallel ? stream.parallel() : stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,6 +128,8 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
private int samplesPerPixel = 100;
|
private int samplesPerPixel = 100;
|
||||||
private int maxDepth = 10;
|
private int maxDepth = 10;
|
||||||
private double gamma = 2.0;
|
private double gamma = 2.0;
|
||||||
|
private boolean parallel = true;
|
||||||
|
private boolean iterative = false;
|
||||||
|
|
||||||
public @NotNull Builder withSamplesPerPixel(int samples) {
|
public @NotNull Builder withSamplesPerPixel(int samples) {
|
||||||
if (samples <= 0) throw new IllegalArgumentException("samples must be positive");
|
if (samples <= 0) throw new IllegalArgumentException("samples must be positive");
|
||||||
@ -119,6 +149,16 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
return this;
|
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() {
|
public @NotNull SimpleRenderer build() {
|
||||||
return new SimpleRenderer(this);
|
return new SimpleRenderer(this);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user