diff --git a/src/main/java/eu/jonahbauer/raytracing/Main.java b/src/main/java/eu/jonahbauer/raytracing/Main.java index 861a988..5cb14f3 100644 --- a/src/main/java/eu/jonahbauer/raytracing/Main.java +++ b/src/main/java/eu/jonahbauer/raytracing/Main.java @@ -1,5 +1,6 @@ package eu.jonahbauer.raytracing; +import eu.jonahbauer.raytracing.material.DielectricMaterial; import eu.jonahbauer.raytracing.material.LambertianMaterial; import eu.jonahbauer.raytracing.material.MetallicMaterial; import eu.jonahbauer.raytracing.render.Camera; @@ -16,7 +17,7 @@ public class Main { var scene = new Scene( new Sphere(0, -100.5, 1, 100, new LambertianMaterial(new Color(0.8, 0.8, 0.0))), new Sphere(0, 0, 1.2, 0.5, new LambertianMaterial(new Color(0.1, 0.2, 0.5))), - new Sphere(-1, 0, 1, 0.5, new MetallicMaterial(new Color(0.8, 0.8, 0.8), 0.3)), + new Sphere(-1, 0, 1, 0.5, new DielectricMaterial(1.5)), new Sphere(1, 0, 1, 0.5, new MetallicMaterial(new Color(0.8, 0.6, 0.2), 1.0)) ); var camera = new Camera(512, 2, 16 / 9d); diff --git a/src/main/java/eu/jonahbauer/raytracing/material/DielectricMaterial.java b/src/main/java/eu/jonahbauer/raytracing/material/DielectricMaterial.java new file mode 100644 index 0000000..14870b0 --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/material/DielectricMaterial.java @@ -0,0 +1,18 @@ +package eu.jonahbauer.raytracing.material; + +import eu.jonahbauer.raytracing.math.Ray; +import eu.jonahbauer.raytracing.math.Vec3; +import eu.jonahbauer.raytracing.render.Color; +import eu.jonahbauer.raytracing.scene.HitResult; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public record DielectricMaterial(double refractionIndex) implements Material { + @Override + public @NotNull Optional scatter(@NotNull Ray ray, @NotNull HitResult hit) { + var ri = hit.frontFace() ? (1 / refractionIndex) : refractionIndex; + var refracted = Vec3.refract(ray.direction(), hit.normal(), ri); + return Optional.of(new ScatterResult(new Ray(hit.position(), refracted), Color.WHITE)); + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java b/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java index 0439bb3..9bb8f85 100644 --- a/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java +++ b/src/main/java/eu/jonahbauer/raytracing/math/Vec3.java @@ -31,6 +31,14 @@ public record Vec3(double x, double y, double z) { return vec.minus(normal.times(2 * normal.times(vec))); } + public static @NotNull Vec3 refract(@NotNull Vec3 vec, @NotNull Vec3 normal, double index) { + vec = vec.unit(); + var cosTheta = Math.min(- vec.times(normal), 1.0); + var rOutPerp = vec.plus(normal.times(cosTheta)).times(index); + var rOutParallel = normal.times(- Math.sqrt(Math.abs(1 - rOutPerp.squared()))); + return rOutPerp.plus(rOutParallel); + } + public @NotNull Vec3 plus(@NotNull Vec3 b) { return new Vec3(this.x + b.x, this.y + b.y, this.z + b.z); } diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/HitResult.java b/src/main/java/eu/jonahbauer/raytracing/scene/HitResult.java index ad2dd79..a3bd1a5 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/HitResult.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/HitResult.java @@ -10,7 +10,8 @@ public record HitResult( double t, @NotNull Vec3 position, @NotNull Vec3 normal, - @NotNull Material material + @NotNull Material material, + boolean frontFace ) implements Comparable { public HitResult { if (t < 0 || !Double.isFinite(t)) throw new IllegalArgumentException("t must be non-negative"); diff --git a/src/main/java/eu/jonahbauer/raytracing/scene/Sphere.java b/src/main/java/eu/jonahbauer/raytracing/scene/Sphere.java index d795ce5..805aff6 100644 --- a/src/main/java/eu/jonahbauer/raytracing/scene/Sphere.java +++ b/src/main/java/eu/jonahbauer/raytracing/scene/Sphere.java @@ -39,7 +39,9 @@ public record Sphere(@NotNull Vec3 center, double radius, @NotNull Material mate if (!range.surrounds(t)) return Optional.empty(); var position = ray.at(t); - return Optional.of(new HitResult(t, position, position.minus(center), material)); + var normal = position.minus(center); + var frontFace = normal.times(ray.direction()) < 0; + return Optional.of(new HitResult(t, position, frontFace ? normal : normal.times(-1), material, frontFace)); } public @NotNull Sphere withCenter(@NotNull Vec3 center) {