Compare commits

...

2 Commits

6 changed files with 113 additions and 28 deletions

View File

@ -1,5 +1,6 @@
package eu.jonahbauer.raytracing; package eu.jonahbauer.raytracing;
import eu.jonahbauer.raytracing.math.AABB;
import eu.jonahbauer.raytracing.math.Vec3; import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.render.texture.CheckerTexture; import eu.jonahbauer.raytracing.render.texture.CheckerTexture;
import eu.jonahbauer.raytracing.render.texture.Color; import eu.jonahbauer.raytracing.render.texture.Color;
@ -162,12 +163,11 @@ public class Examples {
return new Example( return new Example(
new Scene( new Scene(
new Parallelogram(new Vec3(555, 0, 0), new Vec3(0, 555, 0), new Vec3(0, 0, 555), green), new Box(
new Parallelogram(new Vec3(0, 0, 0), new Vec3(0, 555, 0), new Vec3(0, 0, 555), red), new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)),
white, white, red, green, white, null
),
new Parallelogram(new Vec3(343, 554, 332), new Vec3(-130, 0, 0), new Vec3(0, 0, -105), light), new Parallelogram(new Vec3(343, 554, 332), new Vec3(-130, 0, 0), new Vec3(0, 0, -105), light),
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),
new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)), new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)),
new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65)) new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65))
), ),
@ -189,12 +189,11 @@ public class Examples {
return new Example( return new Example(
new Scene( new Scene(
new Parallelogram(new Vec3(555, 0, 0), new Vec3(0, 555, 0), new Vec3(0, 0, 555), green), new Box(
new Parallelogram(new Vec3(0, 0, 0), new Vec3(0, 555, 0), new Vec3(0, 0, 555), red), new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)),
white, white, red, green, white, null
),
new Parallelogram(new Vec3(113, 554, 127), new Vec3(330, 0, 0), new Vec3(0, 0, 305), light), new Parallelogram(new Vec3(113, 554, 127), new Vec3(330, 0, 0), new Vec3(0, 0, 305), light),
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),
new ConstantMedium( new ConstantMedium(
new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)), new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)),
0.01, new IsotropicMaterial(Color.BLACK) 0.01, new IsotropicMaterial(Color.BLACK)

View File

@ -22,7 +22,7 @@ public record DielectricMaterial(double refractionIndex, @NotNull Texture textur
@Override @Override
public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit, @NotNull RandomGenerator random) { public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit, @NotNull RandomGenerator random) {
var ri = hit.frontFace() ? (1 / refractionIndex) : refractionIndex; var ri = hit.isFrontFace() ? (1 / refractionIndex) : refractionIndex;
var cosTheta = Math.min(- ray.direction().unit().times(hit.normal()), 1.0); var cosTheta = Math.min(- ray.direction().unit().times(hit.normal()), 1.0);
var reflectance = reflectance(cosTheta); var reflectance = reflectance(cosTheta);

View File

@ -0,0 +1,67 @@
package eu.jonahbauer.raytracing.render.material;
import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.render.texture.Color;
import eu.jonahbauer.raytracing.render.texture.Texture;
import eu.jonahbauer.raytracing.scene.HitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.random.RandomGenerator;
public final class DirectionalMaterial implements Material {
private final @Nullable Material front;
private final @Nullable Material back;
private final @NotNull Texture texture;
public DirectionalMaterial(@Nullable Material front, @Nullable Material back) {
this.front = front;
this.back = back;
this.texture = new DirectionalTexture(
front != null ? front.texture() : null,
back != null ? back.texture() : null
);
}
@Override
public @NotNull Texture texture() {
return texture;
}
@Override
public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit, @NotNull RandomGenerator random) {
if (hit.isFrontFace()) {
if (front != null) return front.scatter(ray, hit, random);
} else {
if (back != null) return back.scatter(ray, hit, random);
}
// let the ray pass through without obstruction
return Optional.of(new ScatterResult(new Ray(ray.at(hit.t()), ray.direction()), Color.WHITE));
}
@Override
public @NotNull Color emitted(@NotNull HitResult hit) {
if (hit.isFrontFace()) {
if (front != null) return front.emitted(hit);
} else {
if (back != null) return back.emitted(hit);
}
return Color.BLACK;
}
private record DirectionalTexture(@Nullable Texture front, @Nullable Texture back) implements Texture {
@Override
public @NotNull Color get(double u, double v, @NotNull Vec3 p) {
throw new UnsupportedOperationException();
}
@Override
public boolean isUVRequired() {
return front() != null && front().isUVRequired() || back() != null && back().isUVRequired();
}
}
}

View File

@ -8,7 +8,7 @@ import java.util.Objects;
public record HitResult( public record HitResult(
double t, @NotNull Vec3 position, @NotNull Vec3 normal, double t, @NotNull Vec3 position, @NotNull Vec3 normal,
@NotNull Material material, double u, double v, boolean frontFace @NotNull Material material, double u, double v, boolean isFrontFace
) implements Comparable<HitResult> { ) implements Comparable<HitResult> {
public HitResult { public HitResult {
Objects.requireNonNull(position, "position"); Objects.requireNonNull(position, "position");
@ -16,7 +16,7 @@ public record HitResult(
} }
public @NotNull HitResult withPositionAndNormal(@NotNull Vec3 position, @NotNull Vec3 normal) { public @NotNull HitResult withPositionAndNormal(@NotNull Vec3 position, @NotNull Vec3 normal) {
return new HitResult(t, position, normal, material, u, v, frontFace); return new HitResult(t, position, normal, material, u, v, isFrontFace);
} }
@Override @Override

View File

@ -8,21 +8,33 @@ import eu.jonahbauer.raytracing.render.material.Material;
import eu.jonahbauer.raytracing.scene.HitResult; import eu.jonahbauer.raytracing.scene.HitResult;
import eu.jonahbauer.raytracing.scene.Hittable; import eu.jonahbauer.raytracing.scene.Hittable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
public record Box(@NotNull AABB box, @NotNull Material material) implements Hittable { public final class Box implements Hittable {
private final @NotNull AABB box;
public Box { private final @Nullable Material @NotNull[] materials;
Objects.requireNonNull(box, "box");
Objects.requireNonNull(material, "material");
}
public Box(@NotNull Vec3 a, @NotNull Vec3 b, @NotNull Material material) { public Box(@NotNull Vec3 a, @NotNull Vec3 b, @NotNull Material material) {
this(new AABB(a, b), material); this(new AABB(a, b), material);
} }
public Box(@NotNull AABB box, @NotNull Material material) {
this(box, Objects.requireNonNull(material, "material"), material, material, material, material, material);
}
public Box(
@NotNull AABB box,
@Nullable Material top, @Nullable Material bottom,
@Nullable Material left, @Nullable Material right,
@Nullable Material front, @Nullable Material back
) {
this.box = Objects.requireNonNull(box, "box");
this.materials = new Material[] { left, bottom, back, right, top, front };
}
@Override @Override
public @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) { public @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) {
// based on AABB#hit with additional detection of the side hit // based on AABB#hit with additional detection of the side hit
@ -70,22 +82,29 @@ public record Box(@NotNull AABB box, @NotNull Material material) implements Hitt
} }
private @NotNull Optional<HitResult> hit0(double tmin, double tmax, @NotNull Side entry, @NotNull Side exit, @NotNull Ray ray, @NotNull Range range) { private @NotNull Optional<HitResult> hit0(double tmin, double tmax, @NotNull Side entry, @NotNull Side exit, @NotNull Ray ray, @NotNull Range range) {
boolean frontFace;
double t; double t;
if (range.surrounds(tmin)) { Side side;
frontFace = true; boolean frontFace;
Material material;
Vec3 normal;
if (range.surrounds(tmin) && materials[entry.ordinal()] != null) {
t = tmin; t = tmin;
} else if (range.surrounds(tmax)) { side = entry;
frontFace = false; frontFace = true;
material = materials[side.ordinal()];
normal = side.normal;
} else if (range.surrounds(tmax) && materials[exit.ordinal()] != null) {
t = tmax; t = tmax;
side = exit;
frontFace = false;
material = materials[side.ordinal()];
normal = side.normal.neg();
} else { } else {
return Optional.empty(); return Optional.empty();
} }
var side = frontFace ? entry : exit;
var normal = frontFace ? side.normal : side.normal.neg();
var position = ray.at(t); var position = ray.at(t);
var uv = material().texture().isUVRequired(); var uv = material.texture().isUVRequired();
var u = uv ? side.getTextureU(box, position) : Double.NaN; var u = uv ? side.getTextureU(box, position) : Double.NaN;
var v = uv ? side.getTextureV(box, position) : Double.NaN; var v = uv ? side.getTextureV(box, position) : Double.NaN;
return Optional.of(new HitResult(t, position, normal, material, u, v, frontFace)); return Optional.of(new HitResult(t, position, normal, material, u, v, frontFace));

View File

@ -32,7 +32,7 @@ public record ConstantMedium(@NotNull Hittable boundary, double density, @NotNul
if (hitDistance > distance) return Optional.empty(); if (hitDistance > distance) return Optional.empty();
var t = tmin + hitDistance / length; var t = tmin + hitDistance / length;
return Optional.of(new HitResult(t, ray.at(t), Vec3.UNIT_X, material, 0, 0, true)); // arbitrary normal and frontFace return Optional.of(new HitResult(t, ray.at(t), Vec3.UNIT_X, material, 0, 0, true)); // arbitrary normal, u, v and isFrontFace
} }
@Override @Override