switch from octree to binary tree
This commit is contained in:
parent
3a3949f518
commit
9106ccf8b0
@ -3,6 +3,7 @@ package eu.jonahbauer.raytracing.math;
|
|||||||
import eu.jonahbauer.raytracing.scene.Hittable;
|
import eu.jonahbauer.raytracing.scene.Hittable;
|
||||||
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;
|
import java.util.Optional;
|
||||||
|
|
||||||
@ -12,6 +13,9 @@ import java.util.Optional;
|
|||||||
public record AABB(@NotNull Vec3 min, @NotNull Vec3 max) {
|
public record AABB(@NotNull Vec3 min, @NotNull Vec3 max) {
|
||||||
public static final AABB UNIVERSE = new AABB(Vec3.MIN, Vec3.MAX);
|
public static final AABB UNIVERSE = new AABB(Vec3.MIN, Vec3.MAX);
|
||||||
public static final AABB EMPTY = new AABB(Vec3.ZERO, Vec3.ZERO);
|
public static final AABB EMPTY = new AABB(Vec3.ZERO, Vec3.ZERO);
|
||||||
|
public static final Comparator<AABB> X_AXIS = Comparator.comparing(AABB::min, Comparator.comparingDouble(Vec3::x));
|
||||||
|
public static final Comparator<AABB> Y_AXIS = Comparator.comparing(AABB::min, Comparator.comparingDouble(Vec3::y));
|
||||||
|
public static final Comparator<AABB> Z_AXIS = Comparator.comparing(AABB::min, Comparator.comparingDouble(Vec3::z));
|
||||||
|
|
||||||
public AABB {
|
public AABB {
|
||||||
var a = min;
|
var a = min;
|
||||||
@ -58,13 +62,17 @@ public record AABB(@NotNull Vec3 min, @NotNull Vec3 max) {
|
|||||||
|
|
||||||
public @NotNull Optional<Range> intersect(@NotNull Ray ray) {
|
public @NotNull Optional<Range> intersect(@NotNull Ray ray) {
|
||||||
if (this == UNIVERSE) return Optional.of(Range.UNIVERSE);
|
if (this == UNIVERSE) return Optional.of(Range.UNIVERSE);
|
||||||
|
if (this == EMPTY) return Optional.empty();
|
||||||
|
|
||||||
int vmask = ray.vmask();
|
int vmask = ray.vmask();
|
||||||
|
|
||||||
|
var origin = ray.origin();
|
||||||
|
var invDirection = ray.direction().inv();
|
||||||
|
|
||||||
// calculate t values for intersection points of ray with planes through min
|
// calculate t values for intersection points of ray with planes through min
|
||||||
var tmin = intersect(min(), ray);
|
var tmin = intersect(min(), origin, invDirection);
|
||||||
// calculate t values for intersection points of ray with planes through max
|
// calculate t values for intersection points of ray with planes through max
|
||||||
var tmax = intersect(max(), ray);
|
var tmax = intersect(max(), origin, invDirection);
|
||||||
|
|
||||||
// determine range of t for which the ray is inside this voxel
|
// determine range of t for which the ray is inside this voxel
|
||||||
double tlmax = Double.NEGATIVE_INFINITY; // lower limit maximum
|
double tlmax = Double.NEGATIVE_INFINITY; // lower limit maximum
|
||||||
@ -86,10 +94,14 @@ public record AABB(@NotNull Vec3 min, @NotNull Vec3 max) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static double @NotNull[] intersect(@NotNull Vec3 corner, @NotNull Ray ray) {
|
public static double @NotNull[] intersect(@NotNull Vec3 corner, @NotNull Ray ray) {
|
||||||
|
return intersect(corner, ray.origin(), ray.direction().inv());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double @NotNull[] intersect(@NotNull Vec3 corner, @NotNull Vec3 origin, @NotNull Vec3 invDirection) {
|
||||||
return new double[] {
|
return new double[] {
|
||||||
(corner.x() - ray.origin().x()) / ray.direction().x(),
|
(corner.x() - origin.x()) * invDirection.x(),
|
||||||
(corner.y() - ray.origin().y()) / ray.direction().y(),
|
(corner.y() - origin.y()) * invDirection.y(),
|
||||||
(corner.z() - ray.origin().z()) / ray.direction().z(),
|
(corner.z() - origin.z()) * invDirection.z(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,8 @@ public record Range(double min, double max) {
|
|||||||
public boolean surrounds(double value) {
|
public boolean surrounds(double value) {
|
||||||
return min < value && value < max;
|
return min < value && value < max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double size() {
|
||||||
|
return max - min;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,10 @@ public record Vec3(double x, double y, double z) {
|
|||||||
return new Vec3(-x, -y, -z);
|
return new Vec3(-x, -y, -z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NotNull Vec3 inv() {
|
||||||
|
return new Vec3(1 / x, 1 / y, 1 / z);
|
||||||
|
}
|
||||||
|
|
||||||
public @NotNull Vec3 cross(@NotNull Vec3 b) {
|
public @NotNull Vec3 cross(@NotNull Vec3 b) {
|
||||||
return new Vec3(
|
return new Vec3(
|
||||||
this.y() * b.z() - b.y() * this.z(),
|
this.y() * b.z() - b.y() * this.z(),
|
||||||
|
@ -3,17 +3,15 @@ package eu.jonahbauer.raytracing.scene;
|
|||||||
import eu.jonahbauer.raytracing.math.AABB;
|
import eu.jonahbauer.raytracing.math.AABB;
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
import eu.jonahbauer.raytracing.render.Color;
|
import eu.jonahbauer.raytracing.render.Color;
|
||||||
|
import eu.jonahbauer.raytracing.scene.util.HittableBinaryTree;
|
||||||
import eu.jonahbauer.raytracing.scene.util.HittableCollection;
|
import eu.jonahbauer.raytracing.scene.util.HittableCollection;
|
||||||
import eu.jonahbauer.raytracing.scene.util.HittableList;
|
|
||||||
import eu.jonahbauer.raytracing.scene.util.HittableOctree;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class Scene extends HittableCollection {
|
public final class Scene extends HittableCollection {
|
||||||
private final @NotNull HittableOctree octree;
|
private final @NotNull HittableCollection objects;
|
||||||
private final @NotNull HittableList list;
|
|
||||||
private final @NotNull SkyBox background;
|
private final @NotNull SkyBox background;
|
||||||
|
|
||||||
public Scene(@NotNull List<? extends @NotNull Hittable> objects) {
|
public Scene(@NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
@ -25,20 +23,8 @@ public final class Scene extends HittableCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Scene(@NotNull SkyBox background, @NotNull List<? extends @NotNull Hittable> objects) {
|
public Scene(@NotNull SkyBox background, @NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
var bounded = new ArrayList<Hittable>();
|
this.objects = new HittableBinaryTree(objects);
|
||||||
var unbounded = new ArrayList<Hittable>();
|
this.background = Objects.requireNonNull(background);
|
||||||
|
|
||||||
objects.forEach(object -> {
|
|
||||||
if (object.getBoundingBox().isPresent()) {
|
|
||||||
bounded.add(object);
|
|
||||||
} else {
|
|
||||||
unbounded.add(object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.octree = new HittableOctree(bounded);
|
|
||||||
this.list = new HittableList(unbounded);
|
|
||||||
this.background = background;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Scene(@NotNull Hittable @NotNull... objects) {
|
public Scene(@NotNull Hittable @NotNull... objects) {
|
||||||
@ -55,8 +41,7 @@ public final class Scene extends HittableCollection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hit(@NotNull Ray ray, @NotNull State state) {
|
public void hit(@NotNull Ray ray, @NotNull State state) {
|
||||||
octree.hit(ray, state);
|
objects.hit(ray, state);
|
||||||
list.hit(ray, state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
package eu.jonahbauer.raytracing.scene.util;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.AABB;
|
||||||
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
|
import eu.jonahbauer.raytracing.scene.Hittable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class HittableBinaryTree extends HittableCollection {
|
||||||
|
private final @Nullable Hittable left;
|
||||||
|
private final @Nullable Hittable right;
|
||||||
|
private final @NotNull AABB bbox;
|
||||||
|
|
||||||
|
public HittableBinaryTree(@NotNull List<? extends @NotNull Hittable> objects) {
|
||||||
|
bbox = AABB.getBoundingBox(objects).orElse(AABB.EMPTY);
|
||||||
|
if (objects.isEmpty()) {
|
||||||
|
left = null;
|
||||||
|
right = null;
|
||||||
|
} else if (objects.size() == 1) {
|
||||||
|
left = objects.getFirst();
|
||||||
|
right = null;
|
||||||
|
} else if (objects.size() == 2) {
|
||||||
|
left = objects.getFirst();
|
||||||
|
right = objects.getLast();
|
||||||
|
} else {
|
||||||
|
var x = bbox.x().size();
|
||||||
|
var y = bbox.y().size();
|
||||||
|
var z = bbox.z().size();
|
||||||
|
Comparator<AABB> comparator;
|
||||||
|
if (x > y && x > z) {
|
||||||
|
comparator = AABB.X_AXIS;
|
||||||
|
} else if (y > z) {
|
||||||
|
comparator = AABB.Y_AXIS;
|
||||||
|
} else {
|
||||||
|
comparator = AABB.Z_AXIS;
|
||||||
|
}
|
||||||
|
var sorted = objects.stream().sorted(Comparator.comparing(Hittable::getBoundingBox, comparator)).toList();
|
||||||
|
var size = sorted.size();
|
||||||
|
|
||||||
|
left = new HittableBinaryTree(sorted.subList(0, size / 2));
|
||||||
|
right = new HittableBinaryTree(sorted.subList(size / 2, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hit(@NotNull Ray ray, @NotNull State state) {
|
||||||
|
if (!bbox.hit(ray)) return;
|
||||||
|
if (left instanceof HittableCollection coll) {
|
||||||
|
coll.hit(ray, state);
|
||||||
|
} else if (left != null) {
|
||||||
|
hit(state, ray, left);
|
||||||
|
}
|
||||||
|
if (right instanceof HittableCollection coll) {
|
||||||
|
coll.hit(ray, state);
|
||||||
|
} else if (right != null) {
|
||||||
|
hit(state, ray, right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull AABB getBoundingBox() {
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user