improve debuggability

main
jbb01 6 months ago
parent 2a2cf7b642
commit 940e8ebc37

@ -13,6 +13,8 @@ import java.nio.file.Path;
import java.util.function.IntFunction;
public class Main {
public static final boolean DEBUG = false;
public static void main(String[] args) throws IOException {
var config = Config.parse(args);
var example = config.example;

@ -178,4 +178,9 @@ public record Vec3(double x, double y, double z) {
public @NotNull Vec3 withZ(double z) {
return new Vec3(x, y, z);
}
@Override
public @NotNull String toString() {
return "(" + x + "," + y + "," + z + ")";
}
}

@ -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;
}

@ -5,6 +5,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Random;
import static eu.jonahbauer.raytracing.Main.DEBUG;
public record Color(double r, double g, double b) implements Texture {
public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0);
public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
@ -82,6 +84,17 @@ public record Color(double r, double g, double b) implements Texture {
this(red / 255f, green / 255f, blue / 255f);
}
public Color {
if (DEBUG) {
if (!Double.isFinite(r) || !Double.isFinite(g) || !Double.isFinite(b)) {
throw new IllegalArgumentException("r, g and b must be finite");
}
if (r < 0 || g < 0 || b < 0) {
throw new IllegalArgumentException("r, g and b must be non-negative");
}
}
}
public int red() {
return toInt(r);
}

@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public record HitResult(
double t, @NotNull Vec3 position, @NotNull Vec3 normal,
double t, @NotNull Vec3 position, @NotNull Vec3 normal, @NotNull Hittable target,
@NotNull Material material, double u, double v, boolean isFrontFace
) implements Comparable<HitResult> {
public HitResult {
@ -16,7 +16,7 @@ public record HitResult(
}
public @NotNull HitResult withPositionAndNormal(@NotNull Vec3 position, @NotNull Vec3 normal) {
return new HitResult(t, position, normal, material, u, v, isFrontFace);
return new HitResult(t, position, normal, target, material, u, v, isFrontFace);
}
@Override

@ -52,10 +52,15 @@ public abstract class Hittable2D implements Hittable {
var frontFace = denominator < 0;
return Optional.of(new HitResult(
t, position, frontFace ? normal : normal.neg(),
t, position, frontFace ? normal : normal.neg(), this,
material, alpha, beta, frontFace
));
}
protected abstract boolean isInterior(double alpha, double beta);
@Override
public @NotNull String toString() {
return this.getClass().getSimpleName() + "(origin=" + origin + ", u=" + u + ", v=" + v + ")";
}
}

@ -110,7 +110,7 @@ public final class Box implements Hittable, Target {
var uv = material.texture().isUVRequired();
var u = uv ? side.getTextureU(box, position) : Double.NaN;
var v = uv ? side.getTextureV(box, position) : Double.NaN;
return Optional.of(new HitResult(t, position, normal, material, u, v, frontFace));
return Optional.of(new HitResult(t, position, normal, this, material, u, v, frontFace));
}
@Override
@ -173,6 +173,11 @@ public final class Box implements Hittable, Target {
&& box.min().z() < point.z() && point.z() < box.max().z();
}
@Override
public @NotNull String toString() {
return "Box(min=" + box.min() + ", max=" + box.max() + ")";
}
private enum Side {
NEG_X(Vec3.UNIT_X.neg()),
NEG_Y(Vec3.UNIT_Y.neg()),

@ -32,7 +32,7 @@ public record ConstantMedium(@NotNull Hittable boundary, double density, @NotNul
if (hitDistance > distance) return Optional.empty();
var t = tmin + hitDistance / length;
return Optional.of(new HitResult(t, ray.at(t), Vec3.UNIT_X, material, 0, 0, true)); // arbitrary normal, u, v and isFrontFace
return Optional.of(new HitResult(t, ray.at(t), Vec3.UNIT_X, this, material, 0, 0, true)); // arbitrary normal, u, v and isFrontFace
}
@Override

@ -67,7 +67,7 @@ public final class Sphere implements Hittable, Target {
}
return Optional.of(new HitResult(
t, position, frontFace ? normal : normal.neg(),
t, position, frontFace ? normal : normal.neg(), this,
material, u, v, frontFace
));
}
@ -97,4 +97,9 @@ public final class Sphere implements Hittable, Target {
return target.times(radius).plus(center).minus(origin);
}
@Override
public @NotNull String toString() {
return "Sphere(center=" + center + ", radius=" + radius + ")";
}
}

@ -89,6 +89,11 @@ public sealed class RotateY extends Transform {
return bbox;
}
@Override
public @NotNull String toString() {
return object + " rotated by " + Math.toDegrees(Math.atan2(sin, cos)) + "° around the y axis";
}
private static final class RotateYTarget extends RotateY implements Target {
private RotateYTarget(@NotNull Hittable object, double angle) {

@ -48,6 +48,11 @@ public sealed class Translate extends Transform {
return bbox;
}
@Override
public @NotNull String toString() {
return object + " translated by " + offset;
}
private static final class TranslateTarget extends Translate implements Target {
private TranslateTarget(@NotNull Hittable object, @NotNull Vec3 offset) {
super(object, offset);

Loading…
Cancel
Save