|
|
|
@ -14,7 +14,7 @@ import java.util.random.RandomGenerator;
|
|
|
|
|
import java.util.stream.IntStream;
|
|
|
|
|
|
|
|
|
|
public final class SimpleRenderer implements Renderer {
|
|
|
|
|
private final int samplesPerPixel;
|
|
|
|
|
private final int sqrtSamplesPerPixel;
|
|
|
|
|
private final int maxDepth;
|
|
|
|
|
private final double gamma;
|
|
|
|
|
|
|
|
|
@ -30,7 +30,7 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private SimpleRenderer(@NotNull Builder builder) {
|
|
|
|
|
this.samplesPerPixel = builder.samplesPerPixel;
|
|
|
|
|
this.sqrtSamplesPerPixel = (int) Math.sqrt(builder.samplesPerPixel);
|
|
|
|
|
this.maxDepth = builder.maxDepth;
|
|
|
|
|
this.gamma = builder.gamma;
|
|
|
|
|
|
|
|
|
@ -38,6 +38,9 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
|
this.iterative = builder.iterative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@inheritDoc}
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void render(@NotNull Camera camera, @NotNull Scene scene, @NotNull Canvas canvas) {
|
|
|
|
|
if (canvas.getWidth() != camera.getWidth() || canvas.getHeight() != camera.getHeight()) {
|
|
|
|
@ -45,19 +48,35 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iterative) {
|
|
|
|
|
renderIterative(camera, scene, canvas);
|
|
|
|
|
} else {
|
|
|
|
|
renderNonIterative(camera, scene, canvas);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Renders the {@code scene} as seen by the {@code camera} to the {@code canvas}, taking one sample per pixel at
|
|
|
|
|
* a time and updating the canvas after each sample.
|
|
|
|
|
*/
|
|
|
|
|
private void renderIterative(@NotNull Camera camera, @NotNull Scene scene, @NotNull Canvas canvas) {
|
|
|
|
|
var random = new Random();
|
|
|
|
|
|
|
|
|
|
// render one sample after the other
|
|
|
|
|
for (int i = 1 ; i <= samplesPerPixel; i++) {
|
|
|
|
|
var sample = i;
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (int sj = 0; sj < sqrtSamplesPerPixel; sj++) {
|
|
|
|
|
for (int si = 0; si < sqrtSamplesPerPixel; si++) {
|
|
|
|
|
var sample = ++i;
|
|
|
|
|
var sif = si;
|
|
|
|
|
var sjf = sj;
|
|
|
|
|
getScanlineStream(camera.getHeight(), parallel).forEach(y -> {
|
|
|
|
|
for (int x = 0; x < camera.getWidth(); x++) {
|
|
|
|
|
var ray = camera.cast(x, y, random);
|
|
|
|
|
var ray = camera.cast(x, y, sif, sjf, sqrtSamplesPerPixel, random);
|
|
|
|
|
var c = getColor(scene, ray, random);
|
|
|
|
|
canvas.set(x, y, Color.average(canvas.get(x, y), c, sample));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// apply gamma correction
|
|
|
|
|
getScanlineStream(camera.getHeight(), parallel).forEach(y -> {
|
|
|
|
@ -65,23 +84,31 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
|
canvas.set(x, y, Color.gamma(canvas.get(x, y), gamma));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Renders the {@code scene} as seen by the {@code camera} to the {@code canvas}, taking some amount of samples
|
|
|
|
|
* per pixel and updating the canvas after each pixel.
|
|
|
|
|
*/
|
|
|
|
|
private void renderNonIterative(@NotNull Camera camera, @NotNull Scene scene, @NotNull Canvas canvas) {
|
|
|
|
|
var splittable = new SplittableRandom();
|
|
|
|
|
// render one pixel after the other
|
|
|
|
|
getScanlineStream(camera.getHeight(), parallel).forEach(y -> {
|
|
|
|
|
var random = splittable.split();
|
|
|
|
|
for (int x = 0; x < camera.getWidth(); x++) {
|
|
|
|
|
var color = Color.BLACK;
|
|
|
|
|
for (int i = 1; i <= samplesPerPixel; i++) {
|
|
|
|
|
var ray = camera.cast(x, y, random);
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (int sj = 0; sj < sqrtSamplesPerPixel; sj++) {
|
|
|
|
|
for (int si = 0; si < sqrtSamplesPerPixel; si++) {
|
|
|
|
|
var ray = camera.cast(x, y, si, sj, sqrtSamplesPerPixel, random);
|
|
|
|
|
var c = getColor(scene, ray, random);
|
|
|
|
|
color = Color.average(color, c, i);
|
|
|
|
|
color = Color.average(color, c, ++i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
canvas.set(x, y, Color.gamma(color, gamma));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@return the color of the given ray in the given scene}
|
|
|
|
|