add camera, scene and skybox

main
jbb01 6 months ago
parent 028d19b118
commit 7875befa94

@ -1,7 +1,18 @@
package eu.jonahbauer.raytracing; package eu.jonahbauer.raytracing;
import eu.jonahbauer.raytracing.render.Camera;
import eu.jonahbauer.raytracing.render.ImageIO;
import eu.jonahbauer.raytracing.render.Scene;
import java.io.IOException;
import java.nio.file.Path;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) throws IOException {
System.out.println("Hello world!"); var scene = new Scene();
var camera = new Camera(256, 2, 16 / 9d);
var image = scene.render(camera);
ImageIO.write(image, Path.of("scene-" + System.currentTimeMillis() + ".ppm"));
} }
} }

@ -0,0 +1,73 @@
package eu.jonahbauer.raytracing.render;
import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.math.Vec3;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public record Camera(
int width, int height,
double viewportWidth, double viewportHeight,
@NotNull Vec3 origin, @NotNull Vec3 direction
) {
public Camera {
if (width <= 0) throw new IllegalArgumentException("width must be positive");
if (height <= 0) throw new IllegalArgumentException("height must be positive");
if (viewportWidth <= 0 || !Double.isFinite(viewportWidth)) throw new IllegalArgumentException("viewportWidth must be positive");
if (viewportHeight <= 0 || !Double.isFinite(viewportHeight)) throw new IllegalArgumentException("viewportHeight must be positive");
Objects.requireNonNull(origin, "origin");
Objects.requireNonNull(direction, "direction");
}
/**
* Creates a new camera with the given dimensions at the origin facing towards positive z with a focal length of 1.
* @param height the image height
* @param viewportHeight the viewport height
* @param aspectRatio the aspect ratio
*/
public Camera(int height, double viewportHeight, double aspectRatio) {
this((int) (height * aspectRatio), height, viewportHeight * aspectRatio, viewportHeight);
}
/**
* Creates a new camera with the given dimensions at the origin facing towards positive z with a focal length of 1.
* @param width the image width
* @param height the image height
* @param viewportWidth the viewport width
* @param viewportHeight the viewport height
*/
public Camera(int width, int height, double viewportWidth, double viewportHeight) {
this(width, height, viewportWidth, viewportHeight, Vec3.ZERO, Vec3.UNIT_Z);
}
public @NotNull Stream<Pixel> pixels() {
// project direction onto xz-plane
var d = direction.unit();
var dXZ = direction.withY(0).unit();
var viewportU = new Vec3(dXZ.z(), 0, - dXZ.x()); // perpendicular to dXZ in xz-plane
var viewportV = d.cross(viewportU); // perpendicular to viewportU and direction
viewportU = viewportU.times(viewportWidth); // vector along the width of the viewport
viewportV = viewportV.times(- viewportHeight); // vector along the height of the viewport
var pixelU = viewportU.div(width);
var pixelV = viewportV.div(height);
var pixel00 = origin.plus(direction)
.minus(viewportU.div(2)).minus(viewportV.div(2))
.plus(pixelU.div(2)).plus(pixelV.div(2));
return IntStream.range(0, width * height).mapToObj(i -> {
var y = i / width;
var x = i % width;
var pixel = pixel00.plus(pixelU.times(x)).plus(pixelV.times(y));
return new Pixel(x, y, new Ray(origin, pixel.minus(origin)));
});
}
public record Pixel(int x, int y, @NotNull Ray ray) {}
}

@ -5,6 +5,7 @@ import org.jetbrains.annotations.NotNull;
public record Color(double r, double g, double b) { public record Color(double r, double g, double b) {
public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0); 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); public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
public static final @NotNull Color SKY = new Color(0.5, 0.7, 1.0);
public static @NotNull Color lerp(@NotNull Color a, @NotNull Color b, double t) { public static @NotNull Color lerp(@NotNull Color a, @NotNull Color b, double t) {
if (t < 0) return a; if (t < 0) return a;

@ -0,0 +1,30 @@
package eu.jonahbauer.raytracing.render;
import eu.jonahbauer.raytracing.math.Ray;
import org.jetbrains.annotations.NotNull;
public record Scene() {
public @NotNull Image render(@NotNull Camera camera) {
var image = new Image(camera.width(), camera.height());
camera.pixels().forEach(pixel -> {
var x = pixel.x();
var y = pixel.y();
var ray = pixel.ray();
image.set(x, y, getSkyboxColor(ray));
});
return image;
}
private @NotNull Color getSkyboxColor(@NotNull Ray ray) {
// altitude from -pi/2 to pi/2
var alt = Math.copySign(
Math.acos(ray.direction().withY(0).unit().times(ray.direction().unit())),
ray.direction().y()
);
return Color.lerp(Color.WHITE, Color.SKY, alt / Math.PI + 0.5);
}
}
Loading…
Cancel
Save