|
|
@ -8,9 +8,10 @@ import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
|
|
|
import eu.jonahbauer.raytracing.scene.Scene;
|
|
|
|
import eu.jonahbauer.raytracing.scene.Scene;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
import java.util.Random;
|
|
|
|
|
|
|
|
import java.util.SplittableRandom;
|
|
|
|
|
|
|
|
import java.util.random.RandomGenerator;
|
|
|
|
import java.util.stream.IntStream;
|
|
|
|
import java.util.stream.IntStream;
|
|
|
|
import java.util.stream.LongStream;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public final class SimpleRenderer implements Renderer {
|
|
|
|
public final class SimpleRenderer implements Renderer {
|
|
|
|
private final int samplesPerPixel;
|
|
|
|
private final int samplesPerPixel;
|
|
|
@ -44,36 +45,40 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (iterative) {
|
|
|
|
if (iterative) {
|
|
|
|
|
|
|
|
var random = new Random();
|
|
|
|
|
|
|
|
|
|
|
|
// render one sample after the other
|
|
|
|
// render one sample after the other
|
|
|
|
for (int i = 1 ; i <= samplesPerPixel; i++) {
|
|
|
|
for (int i = 1 ; i <= samplesPerPixel; i++) {
|
|
|
|
var sample = i;
|
|
|
|
var sample = i;
|
|
|
|
getPixelStream(camera.getWidth(), camera.getHeight(), parallel).forEach(pixel -> {
|
|
|
|
getScanlineStream(camera.getHeight(), parallel).forEach(y -> {
|
|
|
|
var y = (int) (pixel >> 32);
|
|
|
|
for (int x = 0; x < camera.getWidth(); x++) {
|
|
|
|
var x = (int) pixel;
|
|
|
|
var ray = camera.cast(x, y, random);
|
|
|
|
var ray = camera.cast(x, y);
|
|
|
|
var c = getColor(scene, ray, random);
|
|
|
|
var c = getColor(scene, ray);
|
|
|
|
|
|
|
|
canvas.set(x, y, Color.average(canvas.get(x, y), c, sample));
|
|
|
|
canvas.set(x, y, Color.average(canvas.get(x, y), c, sample));
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// apply gamma correction
|
|
|
|
// apply gamma correction
|
|
|
|
getPixelStream(camera.getWidth(), camera.getHeight(), parallel).forEach(pixel -> {
|
|
|
|
getScanlineStream(camera.getHeight(), parallel).forEach(y -> {
|
|
|
|
var y = (int) (pixel >> 32);
|
|
|
|
for (int x = 0; x < camera.getWidth(); x++) {
|
|
|
|
var x = (int) pixel;
|
|
|
|
|
|
|
|
canvas.set(x, y, Color.gamma(canvas.get(x, y), gamma));
|
|
|
|
canvas.set(x, y, Color.gamma(canvas.get(x, y), gamma));
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
|
|
|
|
var splittable = new SplittableRandom();
|
|
|
|
// render one pixel after the other
|
|
|
|
// render one pixel after the other
|
|
|
|
getPixelStream(camera.getWidth(), camera.getHeight(), parallel).forEach(pixel -> {
|
|
|
|
getScanlineStream(camera.getHeight(), parallel).forEach(y -> {
|
|
|
|
var y = (int) (pixel >> 32);
|
|
|
|
var random = splittable.split();
|
|
|
|
var x = (int) pixel;
|
|
|
|
for (int x = 0; x < camera.getWidth(); x++) {
|
|
|
|
|
|
|
|
|
|
|
|
var color = Color.BLACK;
|
|
|
|
var color = Color.BLACK;
|
|
|
|
for (int i = 1; i <= samplesPerPixel; i++) {
|
|
|
|
for (int i = 1; i <= samplesPerPixel; i++) {
|
|
|
|
var ray = camera.cast(x, y);
|
|
|
|
var ray = camera.cast(x, y, random);
|
|
|
|
var c = getColor(scene, ray);
|
|
|
|
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));
|
|
|
|
canvas.set(x, y, Color.gamma(color, gamma));
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -81,11 +86,11 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* {@return the color of the given ray in the given scene}
|
|
|
|
* {@return the color of the given ray in the given scene}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private @NotNull Color getColor(@NotNull Scene scene, @NotNull Ray ray) {
|
|
|
|
private @NotNull Color getColor(@NotNull Scene scene, @NotNull Ray ray, @NotNull RandomGenerator random) {
|
|
|
|
return getColor0(scene, ray, maxDepth);
|
|
|
|
return getColor0(scene, ray, maxDepth, random);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private @NotNull Color getColor0(@NotNull Scene scene, @NotNull Ray ray, int depth) {
|
|
|
|
private @NotNull Color getColor0(@NotNull Scene scene, @NotNull Ray ray, int depth, @NotNull RandomGenerator random) {
|
|
|
|
var color = Color.BLACK;
|
|
|
|
var color = Color.BLACK;
|
|
|
|
var attenuation = Color.WHITE;
|
|
|
|
var attenuation = Color.WHITE;
|
|
|
|
|
|
|
|
|
|
|
@ -99,7 +104,7 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
var hit = optional.get();
|
|
|
|
var hit = optional.get();
|
|
|
|
var material = hit.material();
|
|
|
|
var material = hit.material();
|
|
|
|
var emitted = material.emitted(hit);
|
|
|
|
var emitted = material.emitted(hit);
|
|
|
|
var scatter = material.scatter(ray, hit);
|
|
|
|
var scatter = material.scatter(ray, hit, random);
|
|
|
|
color = Color.add(color, Color.multiply(attenuation, emitted));
|
|
|
|
color = Color.add(color, Color.multiply(attenuation, emitted));
|
|
|
|
|
|
|
|
|
|
|
|
if (scatter.isEmpty()) break;
|
|
|
|
if (scatter.isEmpty()) break;
|
|
|
@ -114,10 +119,8 @@ 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, boolean parallel) {
|
|
|
|
private static @NotNull IntStream getScanlineStream(int height, boolean parallel) {
|
|
|
|
var stream = IntStream.range(0, height)
|
|
|
|
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;
|
|
|
|
return parallel ? stream.parallel() : stream;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|