add shading according to normal vector
This commit is contained in:
parent
590054a046
commit
14fd1d73fc
@ -4,7 +4,9 @@ import eu.jonahbauer.raytracing.math.Ray;
|
|||||||
import eu.jonahbauer.raytracing.shape.Shape;
|
import eu.jonahbauer.raytracing.shape.Shape;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public record Scene(@NotNull List<@NotNull Shape> shapes) {
|
public record Scene(@NotNull List<@NotNull Shape> shapes) {
|
||||||
|
|
||||||
@ -25,11 +27,17 @@ public record Scene(@NotNull List<@NotNull Shape> shapes) {
|
|||||||
var ray = pixel.ray();
|
var ray = pixel.ray();
|
||||||
|
|
||||||
var result = shapes.stream()
|
var result = shapes.stream()
|
||||||
.mapToDouble(shape -> shape.hit(ray))
|
.map(shape -> shape.hit(ray))
|
||||||
.filter(Double::isFinite)
|
.flatMap(Optional::stream)
|
||||||
.min();
|
.min(Comparator.naturalOrder());
|
||||||
|
|
||||||
if (result.isPresent()) {
|
if (result.isPresent()) {
|
||||||
image.set(x, y, Color.RED);
|
var normal = result.get().normal();
|
||||||
|
image.set(x, y, new Color(
|
||||||
|
0.5 * (normal.x() + 1),
|
||||||
|
0.5 * (normal.y() + 1),
|
||||||
|
0.5 * (normal.z() + 1)
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
image.set(x, y, getSkyboxColor(ray));
|
image.set(x, y, getSkyboxColor(ray));
|
||||||
}
|
}
|
||||||
|
16
src/main/java/eu/jonahbauer/raytracing/shape/HitResult.java
Normal file
16
src/main/java/eu/jonahbauer/raytracing/shape/HitResult.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package eu.jonahbauer.raytracing.shape;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record HitResult(double t, @NotNull Vec3 normal) implements Comparable<HitResult> {
|
||||||
|
public HitResult {
|
||||||
|
if (t < 0 || !Double.isFinite(t)) throw new IllegalArgumentException("t must be non-negative");
|
||||||
|
normal = normal.unit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull HitResult o) {
|
||||||
|
return Double.compare(t, o.t);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@ package eu.jonahbauer.raytracing.shape;
|
|||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public sealed interface Shape permits Sphere {
|
public sealed interface Shape permits Sphere {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,5 +12,5 @@ public sealed interface Shape permits Sphere {
|
|||||||
* the ray origin, or <code>Double.NaN</code> if the ray does not intersect this shape}
|
* the ray origin, or <code>Double.NaN</code> if the ray does not intersect this shape}
|
||||||
* @param ray a ray
|
* @param ray a ray
|
||||||
*/
|
*/
|
||||||
double hit(@NotNull Ray ray);
|
@NotNull Optional<HitResult> hit(@NotNull Ray ray);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import eu.jonahbauer.raytracing.math.Vec3;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public record Sphere(@NotNull Vec3 center, double radius) implements Shape {
|
public record Sphere(@NotNull Vec3 center, double radius) implements Shape {
|
||||||
public static final @NotNull Sphere UNIT = new Sphere(Vec3.ZERO, 1.0);
|
public static final @NotNull Sphere UNIT = new Sphere(Vec3.ZERO, 1.0);
|
||||||
@ -19,7 +20,7 @@ public record Sphere(@NotNull Vec3 center, double radius) implements Shape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double hit(@NotNull Ray ray) {
|
public @NotNull Optional<HitResult> hit(@NotNull Ray ray) {
|
||||||
var oc = ray.origin().minus(center());
|
var oc = ray.origin().minus(center());
|
||||||
|
|
||||||
var a = ray.direction().squared();
|
var a = ray.direction().squared();
|
||||||
@ -27,14 +28,14 @@ public record Sphere(@NotNull Vec3 center, double radius) implements Shape {
|
|||||||
var c = oc.squared() - radius * radius;
|
var c = oc.squared() - radius * radius;
|
||||||
|
|
||||||
var discriminant = b * b - 4 * a * c;
|
var discriminant = b * b - 4 * a * c;
|
||||||
if (discriminant < 0) return Double.NaN;
|
if (discriminant < 0) return Optional.empty();
|
||||||
|
|
||||||
var sd = Math.sqrt(discriminant);
|
var sd = Math.sqrt(discriminant);
|
||||||
|
|
||||||
double t = (- b - sd) / (2 * a);
|
double t = (- b - sd) / (2 * a);
|
||||||
if (t < 0) t = (-b + sd) / (2 * a);
|
if (t < 0) t = (-b + sd) / (2 * a);
|
||||||
if (t < 0) t = Double.NaN;
|
if (t < 0) return Optional.empty();
|
||||||
return t;
|
return Optional.of(new HitResult(t, ray.at(t).minus(center)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull Sphere withCenter(@NotNull Vec3 center) {
|
public @NotNull Sphere withCenter(@NotNull Vec3 center) {
|
||||||
|
@ -18,8 +18,8 @@ class SphereTest {
|
|||||||
var direction = new Vec3(-1, -1, -1);
|
var direction = new Vec3(-1, -1, -1);
|
||||||
var ray = new Ray(origin, direction);
|
var ray = new Ray(origin, direction);
|
||||||
|
|
||||||
var t = sphere.hit(ray);
|
var result = sphere.hit(ray);
|
||||||
assertFalse(Double.isNaN(t));
|
assertFalse(result.isEmpty());
|
||||||
assertEquals(center.plus(new Vec3(1, 1, 1).unit().times(radius)), ray.at(t));
|
assertEquals(center.plus(new Vec3(1, 1, 1).unit().times(radius)), ray.at(result.get().t()));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user