diff --git a/src/main/java/eu/jonahbauer/raytracing/Main.java b/src/main/java/eu/jonahbauer/raytracing/Main.java index fc1eae0..bdf5901 100644 --- a/src/main/java/eu/jonahbauer/raytracing/Main.java +++ b/src/main/java/eu/jonahbauer/raytracing/Main.java @@ -138,8 +138,8 @@ public class Main { new Parallelogram(new Vec3(0, 0, 0), new Vec3(555, 0 ,0), new Vec3(0, 0, 555), white), new Parallelogram(new Vec3(555, 555, 555), new Vec3(-555, 0 ,0), new Vec3(0, 0, -555), white), new Parallelogram(new Vec3(0, 0, 555), new Vec3(555, 0 ,0), new Vec3(0, 555, 0), white), - Hittables.box(new Vec3(130, 0, 65), new Vec3(295, 165, 230), white), - Hittables.box(new Vec3(265, 0, 295), new Vec3(430, 330, 460), white) + Hittables.box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)), + 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) diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java b/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java index 8065941..bd2fa0d 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/Hittable.java @@ -3,6 +3,9 @@ package eu.jonahbauer.raytracing.scene; import eu.jonahbauer.raytracing.math.BoundingBox; import eu.jonahbauer.raytracing.math.Range; import eu.jonahbauer.raytracing.math.Ray; +import eu.jonahbauer.raytracing.math.Vec3; +import eu.jonahbauer.raytracing.scene.transform.RotateY; +import eu.jonahbauer.raytracing.scene.transform.Translate; import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -19,4 +22,12 @@ public interface Hittable { default @NotNull Optional getBoundingBox() { return Optional.empty(); } + + default @NotNull Hittable translate(@NotNull Vec3 offset) { + return new Translate(this, offset); + } + + default @NotNull Hittable rotateY(double angle) { + return new RotateY(this, angle); + } } diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/transform/RotateY.java b/src/main/java/eu/jonahbauer/raytracing/scene/transform/RotateY.java new file mode 100644 index 0000000..a9a2719 --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/scene/transform/RotateY.java @@ -0,0 +1,91 @@ +package eu.jonahbauer.raytracing.scene.transform; + +import eu.jonahbauer.raytracing.math.BoundingBox; +import eu.jonahbauer.raytracing.math.Ray; +import eu.jonahbauer.raytracing.math.Vec3; +import eu.jonahbauer.raytracing.scene.HitResult; +import eu.jonahbauer.raytracing.scene.Hittable; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public final class RotateY extends Transform { + private final double cos; + private final double sin; + + private final @NotNull Optional bbox; + + public RotateY(@NotNull Hittable object, double angle) { + super(object); + this.cos = Math.cos(angle); + this.sin = Math.sin(angle); + + this.bbox = object.getBoundingBox().map(bbox -> { + var min = new Vec3(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE); + var max = new Vec3(- Double.MAX_VALUE, - Double.MAX_VALUE, - Double.MAX_VALUE); + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + var x = i * bbox.max().x() + (1 - i) * bbox.min().x(); + var y = j * bbox.max().y() + (1 - j) * bbox.min().y(); + var z = k * bbox.max().z() + (1 - k) * bbox.min().z(); + + var newx = cos * x + sin * z; + var newz = -sin * x + cos * z; + + var temp = new Vec3(newx, y, newz); + + min = Vec3.min(min, temp); + max = Vec3.max(max, temp); + } + } + } + + return new BoundingBox(min, max); + }); + } + + @Override + protected @NotNull Ray transform(@NotNull Ray ray) { + var origin = ray.origin(); + var direction = ray.direction(); + + var newOrigin = new Vec3( + cos * origin.x() - sin * origin.z(), + origin.y(), + sin * origin.x() + cos * origin.z() + ); + var newDirection = new Vec3( + cos * direction.x() - sin * direction.z(), + direction.y(), + sin * direction.x() + cos * direction.z() + ); + + return new Ray(newOrigin, newDirection); + } + + @Override + protected @NotNull HitResult transform(@NotNull HitResult result) { + var position = result.position(); + var newPosition = new Vec3( + cos * position.x() + sin * position.z(), + position.y(), + - sin * position.x() + cos * position.z() + ); + + var normal = result.normal(); + var newNormal = new Vec3( + cos * normal.x() + sin * normal.z(), + normal.y(), + -sin * normal.x() + cos * normal.z() + ); + + return new HitResult(result.t(), newPosition, newNormal, result.material(), result.frontFace()); + } + + @Override + public @NotNull Optional getBoundingBox() { + return bbox; + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/transform/Transform.java b/src/main/java/eu/jonahbauer/raytracing/scene/transform/Transform.java new file mode 100644 index 0000000..92a0cdc --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/scene/transform/Transform.java @@ -0,0 +1,27 @@ +package eu.jonahbauer.raytracing.scene.transform; + +import eu.jonahbauer.raytracing.math.Range; +import eu.jonahbauer.raytracing.math.Ray; +import eu.jonahbauer.raytracing.scene.HitResult; +import eu.jonahbauer.raytracing.scene.Hittable; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.Optional; + +public abstract class Transform implements Hittable { + protected final @NotNull Hittable object; + + protected Transform(@NotNull Hittable object) { + this.object = Objects.requireNonNull(object); + } + + protected abstract @NotNull Ray transform(@NotNull Ray ray); + + protected abstract @NotNull HitResult transform(@NotNull HitResult result); + + @Override + public final @NotNull Optional hit(@NotNull Ray ray, @NotNull Range range) { + return object.hit(transform(ray), range).map(this::transform); + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/transform/Translate.java b/src/main/java/eu/jonahbauer/raytracing/scene/transform/Translate.java new file mode 100644 index 0000000..dbc52a0 --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/scene/transform/Translate.java @@ -0,0 +1,46 @@ +package eu.jonahbauer.raytracing.scene.transform; + +import eu.jonahbauer.raytracing.math.BoundingBox; +import eu.jonahbauer.raytracing.math.Ray; +import eu.jonahbauer.raytracing.math.Vec3; +import eu.jonahbauer.raytracing.scene.HitResult; +import eu.jonahbauer.raytracing.scene.Hittable; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public final class Translate extends Transform { + private final @NotNull Vec3 offset; + + private final @NotNull Optional bbox; + + public Translate(@NotNull Hittable object, @NotNull Vec3 offset) { + super(object); + this.offset = offset; + this.bbox = object.getBoundingBox().map(bbox -> new BoundingBox( + bbox.min().plus(offset), + bbox.max().plus(offset) + )); + } + + @Override + protected @NotNull Ray transform(@NotNull Ray ray) { + return new Ray(ray.origin().minus(offset), ray.direction()); + } + + @Override + protected @NotNull HitResult transform(@NotNull HitResult result) { + return new HitResult( + result.t(), + result.position().plus(offset), + result.normal(), + result.material(), + result.frontFace() + ); + } + + @Override + public @NotNull Optional getBoundingBox() { + return bbox; + } +}