add sphere
This commit is contained in:
parent
7875befa94
commit
590054a046
@ -3,13 +3,14 @@ package eu.jonahbauer.raytracing;
|
|||||||
import eu.jonahbauer.raytracing.render.Camera;
|
import eu.jonahbauer.raytracing.render.Camera;
|
||||||
import eu.jonahbauer.raytracing.render.ImageIO;
|
import eu.jonahbauer.raytracing.render.ImageIO;
|
||||||
import eu.jonahbauer.raytracing.render.Scene;
|
import eu.jonahbauer.raytracing.render.Scene;
|
||||||
|
import eu.jonahbauer.raytracing.shape.Sphere;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
var scene = new Scene();
|
var scene = new Scene(new Sphere(0, 0, 1, 0.5));
|
||||||
var camera = new Camera(256, 2, 16 / 9d);
|
var camera = new Camera(256, 2, 16 / 9d);
|
||||||
|
|
||||||
var image = scene.render(camera);
|
var image = scene.render(camera);
|
||||||
|
@ -6,6 +6,7 @@ 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 final @NotNull Color SKY = new Color(0.5, 0.7, 1.0);
|
||||||
|
public static final @NotNull Color RED = new Color(1.0, 0.0, 0.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;
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
package eu.jonahbauer.raytracing.render;
|
package eu.jonahbauer.raytracing.render;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import eu.jonahbauer.raytracing.shape.Shape;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public record Scene() {
|
import java.util.List;
|
||||||
|
|
||||||
|
public record Scene(@NotNull List<@NotNull Shape> shapes) {
|
||||||
|
|
||||||
|
public Scene {
|
||||||
|
shapes = List.copyOf(shapes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Scene(@NotNull Shape @NotNull ... shapes) {
|
||||||
|
this(List.of(shapes));
|
||||||
|
}
|
||||||
|
|
||||||
public @NotNull Image render(@NotNull Camera camera) {
|
public @NotNull Image render(@NotNull Camera camera) {
|
||||||
var image = new Image(camera.width(), camera.height());
|
var image = new Image(camera.width(), camera.height());
|
||||||
@ -13,7 +24,15 @@ public record Scene() {
|
|||||||
var y = pixel.y();
|
var y = pixel.y();
|
||||||
var ray = pixel.ray();
|
var ray = pixel.ray();
|
||||||
|
|
||||||
image.set(x, y, getSkyboxColor(ray));
|
var result = shapes.stream()
|
||||||
|
.mapToDouble(shape -> shape.hit(ray))
|
||||||
|
.filter(Double::isFinite)
|
||||||
|
.min();
|
||||||
|
if (result.isPresent()) {
|
||||||
|
image.set(x, y, Color.RED);
|
||||||
|
} else {
|
||||||
|
image.set(x, y, getSkyboxColor(ray));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
14
src/main/java/eu/jonahbauer/raytracing/shape/Shape.java
Normal file
14
src/main/java/eu/jonahbauer/raytracing/shape/Shape.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package eu.jonahbauer.raytracing.shape;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public sealed interface Shape permits Sphere {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the value <code>t</code> such that <code>ray.at(t)</code> is the intersection of this shaped closest to
|
||||||
|
* the ray origin, or <code>Double.NaN</code> if the ray does not intersect this shape}
|
||||||
|
* @param ray a ray
|
||||||
|
*/
|
||||||
|
double hit(@NotNull Ray ray);
|
||||||
|
}
|
51
src/main/java/eu/jonahbauer/raytracing/shape/Sphere.java
Normal file
51
src/main/java/eu/jonahbauer/raytracing/shape/Sphere.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package eu.jonahbauer.raytracing.shape;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record Sphere(@NotNull Vec3 center, double radius) implements Shape {
|
||||||
|
public static final @NotNull Sphere UNIT = new Sphere(Vec3.ZERO, 1.0);
|
||||||
|
|
||||||
|
public Sphere {
|
||||||
|
Objects.requireNonNull(center, "center");
|
||||||
|
if (radius <= 0 || !Double.isFinite(radius)) throw new IllegalArgumentException("radius must be positive");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sphere(double x, double y, double z, double r) {
|
||||||
|
this(new Vec3(x, y, z), r);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double hit(@NotNull Ray ray) {
|
||||||
|
var oc = ray.origin().minus(center());
|
||||||
|
|
||||||
|
var a = ray.direction().squared();
|
||||||
|
var b = 2 * ray.direction().times(oc);
|
||||||
|
var c = oc.squared() - radius * radius;
|
||||||
|
|
||||||
|
var discriminant = b * b - 4 * a * c;
|
||||||
|
if (discriminant < 0) return Double.NaN;
|
||||||
|
|
||||||
|
var sd = Math.sqrt(discriminant);
|
||||||
|
|
||||||
|
double t = (- b - sd) / (2 * a);
|
||||||
|
if (t < 0) t = (-b + sd) / (2 * a);
|
||||||
|
if (t < 0) t = Double.NaN;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Sphere withCenter(@NotNull Vec3 center) {
|
||||||
|
return new Sphere(center, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Sphere withCenter(double x, double y, double z) {
|
||||||
|
return withCenter(new Vec3(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Sphere withRadius(double radius) {
|
||||||
|
return new Sphere(center, radius);
|
||||||
|
}
|
||||||
|
}
|
25
src/test/java/eu/jonahbauer/raytracing/shape/SphereTest.java
Normal file
25
src/test/java/eu/jonahbauer/raytracing/shape/SphereTest.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package eu.jonahbauer.raytracing.shape;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class SphereTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hit() {
|
||||||
|
var center = new Vec3(1, 2, 3);
|
||||||
|
var radius = 5;
|
||||||
|
var sphere = new Sphere(center, radius);
|
||||||
|
|
||||||
|
var origin = new Vec3(6, 7, 8);
|
||||||
|
var direction = new Vec3(-1, -1, -1);
|
||||||
|
var ray = new Ray(origin, direction);
|
||||||
|
|
||||||
|
var t = sphere.hit(ray);
|
||||||
|
assertFalse(Double.isNaN(t));
|
||||||
|
assertEquals(center.plus(new Vec3(1, 1, 1).unit().times(radius)), ray.at(t));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user