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.camera.Camera;
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.LiveCanvas;
import eu.jonahbauer.raytracing.render.material.*;
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.hittable3d.ConstantMedium;
import eu.jonahbauer.raytracing.scene.hittable3d.Sphere;
@ -17,33 +20,140 @@ import eu.jonahbauer.raytracing.scene.util.Hittables;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Random;
import java.util.function.IntFunction;
public class Main {
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 camera = example.camera();
var renderer = SimpleRenderer.builder()
.withSamplesPerPixel(1000)
.withMaxDepth(50)
.withIterative(true)
.withSamplesPerPixel(config.samples)
.withMaxDepth(config.depth)
.withIterative(config.iterative)
.build();
Canvas canvas;
if (config.preview) {
var image = new LiveCanvas(new Image(camera.getWidth(), camera.getHeight()));
image.preview();
canvas = image;
} else {
canvas = new Image(camera.getWidth(), camera.getHeight());
}
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);
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 objects = new ArrayList<Hittable>();
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))));
var camera = SimpleCamera.builder()
.withImage(1200, 675)
.withImage(height * 16 / 9, height)
.withPosition(new Vec3(13, 2, 3))
.withTarget(new Vec3(0, 0, 0))
.withFieldOfView(Math.toRadians(20))
@ -89,7 +199,8 @@ public class Main {
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(
new Scene(
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)))
),
SimpleCamera.builder()
.withImage(400, 400)
.withImage(height, height)
.withFieldOfView(Math.toRadians(80))
.withPosition(new Vec3(0, 0, 9))
.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(
new Scene(
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)))
),
SimpleCamera.builder()
.withImage(400, 225)
.withImage(height * 16 / 9, height)
.withFieldOfView(Math.toRadians(20))
.withPosition(new Vec3(26, 3, 6))
.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 white = new LambertianMaterial(new Color(.73, .73, .73));
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))
),
SimpleCamera.builder()
.withImage(600, 600)
.withImage(height, height)
.withFieldOfView(Math.toRadians(40))
.withPosition(new Vec3(278, 278, -800))
.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 white = new LambertianMaterial(new Color(.73, .73, .73));
var green = new LambertianMaterial(new Color(.12, .45, .15));
@ -175,7 +290,7 @@ public class Main {
)
),
SimpleCamera.builder()
.withImage(600, 600)
.withImage(height, height)
.withFieldOfView(Math.toRadians(40))
.withPosition(new Vec3(278, 278, -800))
.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() {
return SkyBox.gradient(new Color(0.5, 0.7, 1.0), Color.WHITE);
}
}
private record Example(@NotNull Scene scene, @NotNull Camera camera) {}
}
Loading…
Cancel
Save