add command line argument parser

main
jbb01 6 months ago
parent 360fb2c990
commit e4e241a314

@ -5,11 +5,14 @@ import eu.jonahbauer.raytracing.render.Color;
import eu.jonahbauer.raytracing.render.ImageFormat; import eu.jonahbauer.raytracing.render.ImageFormat;
import eu.jonahbauer.raytracing.render.camera.Camera; import eu.jonahbauer.raytracing.render.camera.Camera;
import eu.jonahbauer.raytracing.render.camera.SimpleCamera; import eu.jonahbauer.raytracing.render.camera.SimpleCamera;
import eu.jonahbauer.raytracing.render.canvas.Canvas;
import eu.jonahbauer.raytracing.render.canvas.Image; import eu.jonahbauer.raytracing.render.canvas.Image;
import eu.jonahbauer.raytracing.render.canvas.LiveCanvas; import eu.jonahbauer.raytracing.render.canvas.LiveCanvas;
import eu.jonahbauer.raytracing.render.material.*; import eu.jonahbauer.raytracing.render.material.*;
import eu.jonahbauer.raytracing.render.renderer.SimpleRenderer; import eu.jonahbauer.raytracing.render.renderer.SimpleRenderer;
import eu.jonahbauer.raytracing.scene.*; import eu.jonahbauer.raytracing.scene.Hittable;
import eu.jonahbauer.raytracing.scene.Scene;
import eu.jonahbauer.raytracing.scene.SkyBox;
import eu.jonahbauer.raytracing.scene.hittable2d.Parallelogram; import eu.jonahbauer.raytracing.scene.hittable2d.Parallelogram;
import eu.jonahbauer.raytracing.scene.hittable3d.ConstantMedium; import eu.jonahbauer.raytracing.scene.hittable3d.ConstantMedium;
import eu.jonahbauer.raytracing.scene.hittable3d.Sphere; import eu.jonahbauer.raytracing.scene.hittable3d.Sphere;
@ -17,33 +20,140 @@ import eu.jonahbauer.raytracing.scene.util.Hittables;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Random; import java.util.Random;
import java.util.function.IntFunction;
public class Main { public class Main {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
var example = getCornellBoxSmoke(); var config = Config.parse(args);
var example = config.example;
var scene = example.scene(); var scene = example.scene();
var camera = example.camera(); var camera = example.camera();
var renderer = SimpleRenderer.builder() var renderer = SimpleRenderer.builder()
.withSamplesPerPixel(1000) .withSamplesPerPixel(config.samples)
.withMaxDepth(50) .withMaxDepth(config.depth)
.withIterative(true) .withIterative(config.iterative)
.build(); .build();
Canvas canvas;
if (config.preview) {
var image = new LiveCanvas(new Image(camera.getWidth(), camera.getHeight())); var image = new LiveCanvas(new Image(camera.getWidth(), camera.getHeight()));
image.preview(); image.preview();
canvas = image;
} else {
canvas = new Image(camera.getWidth(), camera.getHeight());
}
long time = System.nanoTime(); long time = System.nanoTime();
renderer.render(camera, scene, image); renderer.render(camera, scene, canvas);
System.out.printf("rendering finished after %dms", (System.nanoTime() - time) / 1_000_000); System.out.printf("rendering finished after %dms", (System.nanoTime() - time) / 1_000_000);
ImageFormat.PNG.write(image, Path.of("scene-" + System.currentTimeMillis() + ".png")); ImageFormat.PNG.write(canvas, config.path);
}
private record Config(@NotNull Example example, @NotNull Path path, boolean preview, boolean iterative, int samples, int depth) {
public static @NotNull Config parse(@NotNull String @NotNull[] args) {
IntFunction<Example> example = null;
Path path = null;
boolean preview = true;
boolean iterative = false;
int samples = 1000;
int depth = 50;
int height = -1;
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "--output" -> {
if (i + 1 == args.length) throw fail("missing value for parameter --output");
try {
path = Path.of(args[++i]);
} catch (InvalidPathException ex) {
throw fail("value " + args[i] + " is not a valid path");
}
}
case "--preview" -> preview = true;
case "--no-preview" -> preview = false;
case "--iterative" -> iterative = true;
case "--no-iterative" -> iterative = false;
case "--samples" -> {
if (i + 1 == args.length) throw fail("missing value for parameter --samples");
try {
samples = Integer.parseInt(args[++i]);
if (samples <= 0) throw fail("samples must be positive");
} catch (NumberFormatException ex) {
throw fail("value " + args[i] + " is not a valid integer");
}
}
case "--depth" -> {
if (i + 1 == args.length) throw fail("missing value for parameter --depth");
try {
depth = Integer.parseInt(args[++i]);
if (depth <= 0) throw fail("depth must be positive");
} catch (NumberFormatException ex) {
throw fail("value " + args[i] + " is not a valid integer");
}
}
case "--height" -> {
if (i + 1 == args.length) throw fail("missing value for parameter --height");
try {
height = Integer.parseInt(args[++i]);
if (height <= 0) throw fail("height must be positive");
} catch (NumberFormatException ex) {
throw fail("value " + args[i] + " is not a valid integer");
}
}
case String str when !str.startsWith("-") -> example = switch (str) {
case "SIMPLE" -> Examples::getSimpleScene;
case "SPHERES" -> Examples::getSpheres;
case "SQUARES" -> Examples::getSquares;
case "LIGHT" -> Examples::getLight;
case "CORNELL" -> Examples::getCornellBox;
case "CORNELL_SMOKE" -> Examples::getCornellBoxSmoke;
default -> throw fail("unknown example " + str + ", expected one of SIMPLE, SPHERES, SQUARES, LIGHT, CORNELL or CORNELL_SMOKE");
};
default -> throw fail("unknown option " + args[i]);
}
}
if (example == null) example = Examples::getCornellBoxSmoke;
if (path == null) path = Path.of("scene-" + System.currentTimeMillis() + ".png");
return new Config(example.apply(height), path, preview, iterative, samples, depth);
}
private static @NotNull RuntimeException fail(@NotNull String message) {
System.err.println(message);
System.exit(1);
return new RuntimeException();
}
} }
private static @NotNull Example getSpheres() { private static class Examples {
public static @NotNull Example getSimpleScene(int height) {
if (height <= 0) height = 675;
return new Example(
new Scene(
getSkyBox(),
new Sphere(new Vec3(0, -100.5, -1.0), 100.0, new LambertianMaterial(new Color(0.8, 0.8, 0.0))),
new Sphere(new Vec3(0, 0, -1.2), 0.5, new LambertianMaterial(new Color(0.1, 0.2, 0.5))),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.5, new DielectricMaterial(1.5)),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.4, new DielectricMaterial(1 / 1.5)),
new Sphere(new Vec3(1.0, 0, -1.2), 0.5, new MetallicMaterial(new Color(0.8, 0.6, 0.2), 0.0))
),
SimpleCamera.builder()
.withImage(height * 16 / 9, height)
.build()
);
}
public static @NotNull Example getSpheres(int height) {
if (height <= 0) height = 675;
var rng = new Random(1); var rng = new Random(1);
var objects = new ArrayList<Hittable>(); var objects = new ArrayList<Hittable>();
objects.add(new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new Color(0.5, 0.5, 0.5)))); objects.add(new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new Color(0.5, 0.5, 0.5))));
@ -78,7 +188,7 @@ public class Main {
objects.add(new Sphere(new Vec3(4, 1, 0), 1.0, new MetallicMaterial(new Color(0.7, 0.6, 0.5)))); objects.add(new Sphere(new Vec3(4, 1, 0), 1.0, new MetallicMaterial(new Color(0.7, 0.6, 0.5))));
var camera = SimpleCamera.builder() var camera = SimpleCamera.builder()
.withImage(1200, 675) .withImage(height * 16 / 9, height)
.withPosition(new Vec3(13, 2, 3)) .withPosition(new Vec3(13, 2, 3))
.withTarget(new Vec3(0, 0, 0)) .withTarget(new Vec3(0, 0, 0))
.withFieldOfView(Math.toRadians(20)) .withFieldOfView(Math.toRadians(20))
@ -89,7 +199,8 @@ public class Main {
return new Example(new Scene(getSkyBox(), objects), camera); return new Example(new Scene(getSkyBox(), objects), camera);
} }
private static @NotNull Example getSquares() { public static @NotNull Example getSquares(int height) {
if (height <= 0) height = 600;
return new Example( return new Example(
new Scene( new Scene(
getSkyBox(), getSkyBox(),
@ -100,7 +211,7 @@ public class Main {
new Parallelogram(new Vec3(-2, -3, 5), new Vec3(4, 0, 0), new Vec3(0, 0, -4), new LambertianMaterial(new Color(0.2, 0.8, 0.8))) new Parallelogram(new Vec3(-2, -3, 5), new Vec3(4, 0, 0), new Vec3(0, 0, -4), new LambertianMaterial(new Color(0.2, 0.8, 0.8)))
), ),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(400, 400) .withImage(height, height)
.withFieldOfView(Math.toRadians(80)) .withFieldOfView(Math.toRadians(80))
.withPosition(new Vec3(0, 0, 9)) .withPosition(new Vec3(0, 0, 9))
.withTarget(new Vec3(0, 0, 0)) .withTarget(new Vec3(0, 0, 0))
@ -108,7 +219,8 @@ public class Main {
); );
} }
private static @NotNull Example getLight() { public static @NotNull Example getLight(int height) {
if (height <= 0) height = 225;
return new Example( return new Example(
new Scene( new Scene(
new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new Color(0.2, 0.2, 0.2))), new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new Color(0.2, 0.2, 0.2))),
@ -117,7 +229,7 @@ public class Main {
new Sphere(new Vec3(0, 7, 0), 2, new DiffuseLight(new Color(4.0, 4.0, 4.0))) new Sphere(new Vec3(0, 7, 0), 2, new DiffuseLight(new Color(4.0, 4.0, 4.0)))
), ),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(400, 225) .withImage(height * 16 / 9, height)
.withFieldOfView(Math.toRadians(20)) .withFieldOfView(Math.toRadians(20))
.withPosition(new Vec3(26, 3, 6)) .withPosition(new Vec3(26, 3, 6))
.withTarget(new Vec3(0, 2, 0)) .withTarget(new Vec3(0, 2, 0))
@ -125,7 +237,9 @@ public class Main {
); );
} }
private static @NotNull Example getCornellBox() { public static @NotNull Example getCornellBox(int height) {
if (height <= 0) height = 600;
var red = new LambertianMaterial(new Color(.65, .05, .05)); var red = new LambertianMaterial(new Color(.65, .05, .05));
var white = new LambertianMaterial(new Color(.73, .73, .73)); var white = new LambertianMaterial(new Color(.73, .73, .73));
var green = new LambertianMaterial(new Color(.12, .45, .15)); var green = new LambertianMaterial(new Color(.12, .45, .15));
@ -143,7 +257,7 @@ public class Main {
Hittables.box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65)) Hittables.box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65))
), ),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(600, 600) .withImage(height, height)
.withFieldOfView(Math.toRadians(40)) .withFieldOfView(Math.toRadians(40))
.withPosition(new Vec3(278, 278, -800)) .withPosition(new Vec3(278, 278, -800))
.withTarget(new Vec3(278, 278, 0)) .withTarget(new Vec3(278, 278, 0))
@ -151,7 +265,8 @@ public class Main {
); );
} }
private static @NotNull Example getCornellBoxSmoke() { public static @NotNull Example getCornellBoxSmoke(int height) {
if (height <= 0) height = 600;
var red = new LambertianMaterial(new Color(.65, .05, .05)); var red = new LambertianMaterial(new Color(.65, .05, .05));
var white = new LambertianMaterial(new Color(.73, .73, .73)); var white = new LambertianMaterial(new Color(.73, .73, .73));
var green = new LambertianMaterial(new Color(.12, .45, .15)); var green = new LambertianMaterial(new Color(.12, .45, .15));
@ -175,7 +290,7 @@ public class Main {
) )
), ),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(600, 600) .withImage(height, height)
.withFieldOfView(Math.toRadians(40)) .withFieldOfView(Math.toRadians(40))
.withPosition(new Vec3(278, 278, -800)) .withPosition(new Vec3(278, 278, -800))
.withTarget(new Vec3(278, 278, 0)) .withTarget(new Vec3(278, 278, 0))
@ -183,20 +298,10 @@ public class Main {
); );
} }
private static @NotNull Scene getSimpleScene() {
return new Scene(
getSkyBox(),
new Sphere(new Vec3(0, -100.5, -1.0), 100.0, new LambertianMaterial(new Color(0.8, 0.8, 0.0))),
new Sphere(new Vec3(0, 0, -1.2), 0.5, new LambertianMaterial(new Color(0.1, 0.2, 0.5))),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.5, new DielectricMaterial(1.5)),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.4, new DielectricMaterial(1 / 1.5)),
new Sphere(new Vec3(1.0, 0, -1.2), 0.5, new MetallicMaterial(new Color(0.8, 0.6, 0.2), 0.0))
);
}
private static @NotNull SkyBox getSkyBox() { private static @NotNull SkyBox getSkyBox() {
return SkyBox.gradient(new Color(0.5, 0.7, 1.0), Color.WHITE); return SkyBox.gradient(new Color(0.5, 0.7, 1.0), Color.WHITE);
} }
}
private record Example(@NotNull Scene scene, @NotNull Camera camera) {} private record Example(@NotNull Scene scene, @NotNull Camera camera) {}
} }
Loading…
Cancel
Save