|
|
|
@ -16,6 +16,8 @@ import java.util.SplittableRandom;
|
|
|
|
|
import java.util.random.RandomGenerator;
|
|
|
|
|
import java.util.stream.IntStream;
|
|
|
|
|
|
|
|
|
|
import static eu.jonahbauer.raytracing.Main.DEBUG;
|
|
|
|
|
|
|
|
|
|
public final class SimpleRenderer implements Renderer {
|
|
|
|
|
private final int sqrtSamplesPerPixel;
|
|
|
|
|
private final int maxDepth;
|
|
|
|
@ -104,6 +106,9 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
|
for (int sj = 0; sj < sqrtSamplesPerPixel; sj++) {
|
|
|
|
|
for (int si = 0; si < sqrtSamplesPerPixel; si++) {
|
|
|
|
|
var ray = camera.cast(x, y, si, sj, sqrtSamplesPerPixel, random);
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println("Casting ray " + ray + " through pixel (" + x + "," + y + ") at subpixel (" + si + "," + sj + ")...");
|
|
|
|
|
}
|
|
|
|
|
var c = getColor(scene, ray, random);
|
|
|
|
|
color = Color.average(color, c, ++i);
|
|
|
|
|
}
|
|
|
|
@ -127,38 +132,77 @@ public final class SimpleRenderer implements Renderer {
|
|
|
|
|
while (depth-- > 0) {
|
|
|
|
|
var optional = scene.hit(ray, new Range(0.001, Double.POSITIVE_INFINITY));
|
|
|
|
|
if (optional.isEmpty()) {
|
|
|
|
|
color = Color.add(color, Color.multiply(attenuation, scene.getBackgroundColor(ray)));
|
|
|
|
|
var background = scene.getBackgroundColor(ray);
|
|
|
|
|
color = Color.add(color, Color.multiply(attenuation, background));
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Hit background: " + background);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hit = optional.get();
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Hit " + hit.target() + " at t=" + hit.t() + " (" + hit.position() + ")");
|
|
|
|
|
}
|
|
|
|
|
var material = hit.material();
|
|
|
|
|
var emitted = material.emitted(hit);
|
|
|
|
|
if (DEBUG && !Color.BLACK.equals(emitted)) {
|
|
|
|
|
System.out.println(" Emitted: " + emitted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = material.scatter(ray, hit, random);
|
|
|
|
|
color = Color.add(color, Color.multiply(attenuation, emitted));
|
|
|
|
|
|
|
|
|
|
if (result.isEmpty()) break;
|
|
|
|
|
if (result.isEmpty()) {
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Absorbed");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (result.get()) {
|
|
|
|
|
case Material.SpecularScatterResult(var a, var scattered) -> {
|
|
|
|
|
attenuation = Color.multiply(attenuation, a);
|
|
|
|
|
ray = scattered;
|
|
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Specular scattering with albedo " + a);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case Material.PdfScatterResult(var a, var pdf) -> {
|
|
|
|
|
if (scene.getLights() == null) {
|
|
|
|
|
attenuation = Color.multiply(attenuation, a);
|
|
|
|
|
ray = new Ray(hit.position(), pdf.generate(random));
|
|
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Pdf scattering with albedo " + a);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
var mixed = new MixtureProbabilityDensityFunction(new TargetingProbabilityDensityFunction(hit.position(), scene.getLights()), pdf);
|
|
|
|
|
var mixed = new MixtureProbabilityDensityFunction(new TargetingProbabilityDensityFunction(hit.position(), scene.getLights()), pdf, 0.5);
|
|
|
|
|
var direction = mixed.generate(random);
|
|
|
|
|
var factor = pdf.value(direction) / mixed.value(direction);
|
|
|
|
|
attenuation = Color.multiply(attenuation, Color.multiply(a, factor));
|
|
|
|
|
ray = new Ray(hit.position(), direction);
|
|
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Pdf scattering with albedo " + a + " and factor " + factor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Combined color is " + color);
|
|
|
|
|
System.out.println(" Combined attenuation is " + attenuation);
|
|
|
|
|
System.out.println(" New ray is " + ray);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DEBUG) {
|
|
|
|
|
System.out.println(" Final color is " + color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|