add depth of field

main
jbb01 6 months ago
parent d89d15f1a4
commit 1080711229

@ -28,6 +28,8 @@ public class Main {
.withPosition(new Vec3(-2, 2, 1)) .withPosition(new Vec3(-2, 2, 1))
.withTarget(new Vec3(0, 0, -1)) .withTarget(new Vec3(0, 0, -1))
.withFieldOfView(Math.toRadians(20)) .withFieldOfView(Math.toRadians(20))
.withFocusDistance(3.4)
.withBlurAngle(Math.toRadians(10))
.build(); .build();
var image = camera.render(scene); var image = camera.render(scene);

@ -21,6 +21,7 @@ public final class Camera {
private final int samplesPerPixel; private final int samplesPerPixel;
private final int maxDepth; private final int maxDepth;
private final double gamma; private final double gamma;
private final double blurRadius;
// internal properties // internal properties
private final @NotNull Vec3 u; private final @NotNull Vec3 u;
@ -42,7 +43,7 @@ public final class Camera {
this.width = builder.imageWidth; this.width = builder.imageWidth;
this.height = builder.imageHeight; this.height = builder.imageHeight;
var viewportHeight = 2 * Math.tan(0.5 * builder.fov); var viewportHeight = 2 * Math.tan(0.5 * builder.fov) * builder.focusDistance;
var viewportWidth = viewportHeight * ((double) width / height); var viewportWidth = viewportHeight * ((double) width / height);
this.origin = builder.position; this.origin = builder.position;
@ -51,6 +52,7 @@ public final class Camera {
this.samplesPerPixel = builder.samplePerPixel; this.samplesPerPixel = builder.samplePerPixel;
this.maxDepth = builder.maxDepth; this.maxDepth = builder.maxDepth;
this.gamma = builder.gamma; this.gamma = builder.gamma;
this.blurRadius = Math.tan(0.5 * builder.blurAngle) * builder.focusDistance;
// project direction the horizontal plane // project direction the horizontal plane
var dXZ = direction.withY(0).unit(); var dXZ = direction.withY(0).unit();
@ -63,7 +65,7 @@ public final class Camera {
this.pixelU = u.times(viewportWidth / width); this.pixelU = u.times(viewportWidth / width);
this.pixelV = v.times(viewportHeight / height); this.pixelV = v.times(viewportHeight / height);
this.pixel00 = origin.plus(direction) this.pixel00 = origin.plus(direction.times(builder.focusDistance))
.minus(u.times(0.5 * viewportWidth)).minus(v.times(0.5 * viewportHeight)) .minus(u.times(0.5 * viewportWidth)).minus(v.times(0.5 * viewportHeight))
.plus(pixelU.div(2)).plus(pixelV.div(2)); .plus(pixelU.div(2)).plus(pixelV.div(2));
} }
@ -97,6 +99,17 @@ public final class Camera {
} }
private @NotNull Ray getRay(int x, int y) { private @NotNull Ray getRay(int x, int y) {
var origin = this.origin;
if (blurRadius > 0) {
double bu, bv;
do {
bu = 2 * Math.random() - 1;
bv = 2 * Math.random() - 1;
} while (bu * bu + bv * bv >= 1);
origin = origin.plus(u.times(blurRadius * bu)).plus(v.times(blurRadius * bv));
}
return new Ray(origin, getPixel(x, y).minus(origin)); return new Ray(origin, getPixel(x, y).minus(origin));
} }
@ -147,6 +160,8 @@ public final class Camera {
private double rotation = 0.0; private double rotation = 0.0;
private double fov = 0.5 * Math.PI; private double fov = 0.5 * Math.PI;
private double focusDistance = 10;
private double blurAngle = 0.0;
private int samplePerPixel = 100; private int samplePerPixel = 100;
private int maxDepth = 10; private int maxDepth = 10;
@ -190,6 +205,18 @@ public final class Camera {
return this; return this;
} }
public @NotNull Builder withFocusDistance(double focusDistance) {
if (focusDistance <= 0 || !Double.isFinite(focusDistance)) throw new IllegalArgumentException("focus distance must be positive");
this.focusDistance = focusDistance;
return this;
}
public @NotNull Builder withBlurAngle(double angle) {
if (angle < 0 || angle >= Math.PI || !Double.isFinite(angle)) throw new IllegalArgumentException("blur-angle must be in the range [0, π)");
this.blurAngle = angle;
return this;
}
public @NotNull Builder withSamplesPerPixel(int samples) { public @NotNull Builder withSamplesPerPixel(int samples) {
if (samples <= 0) throw new IllegalArgumentException("samples must be positive"); if (samples <= 0) throw new IllegalArgumentException("samples must be positive");
this.samplePerPixel = samples; this.samplePerPixel = samples;

Loading…
Cancel
Save