add custom Materials
This commit is contained in:
parent
927f63adf0
commit
2e5659a04a
@ -1,6 +1,8 @@
|
||||
package eu.jonahbauer.raytracing;
|
||||
|
||||
import eu.jonahbauer.raytracing.material.LambertianMaterial;
|
||||
import eu.jonahbauer.raytracing.render.Camera;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.ImageFormat;
|
||||
import eu.jonahbauer.raytracing.scene.Scene;
|
||||
import eu.jonahbauer.raytracing.scene.Sphere;
|
||||
@ -11,8 +13,8 @@ import java.nio.file.Path;
|
||||
public class Main {
|
||||
public static void main(String[] args) throws IOException {
|
||||
var scene = new Scene(
|
||||
new Sphere(0, 0, 1, 0.5),
|
||||
new Sphere(0, -100.5, 1, 100)
|
||||
new Sphere(0, 0, 1, 0.5, new LambertianMaterial(Color.RED)),
|
||||
new Sphere(0, -100.5, 1, 100, new LambertianMaterial(Color.BLUE))
|
||||
);
|
||||
var camera = new Camera(512, 2, 16 / 9d);
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
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.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public record LambertianMaterial(@NotNull Color albedo) implements Material {
|
||||
public LambertianMaterial {
|
||||
Objects.requireNonNull(albedo, "albedo");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit) {
|
||||
var newDirection = hit.normal().plus(Vec3.random(true));
|
||||
if (newDirection.isNearZero()) newDirection = hit.normal();
|
||||
|
||||
var scattered = new Ray(hit.position(), newDirection);
|
||||
return Optional.of(new ScatterResult(scattered, albedo));
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package eu.jonahbauer.raytracing.material;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface Material {
|
||||
|
||||
@NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit);
|
||||
|
||||
record ScatterResult(@NotNull Ray ray, @NotNull Color attenuation) {
|
||||
public ScatterResult {
|
||||
Objects.requireNonNull(ray, "ray");
|
||||
Objects.requireNonNull(attenuation, "attenuation");
|
||||
}
|
||||
}
|
||||
}
|
@ -62,6 +62,11 @@ public record Vec3(double x, double y, double z) {
|
||||
public double length() {
|
||||
return Math.sqrt(squared());
|
||||
}
|
||||
|
||||
public boolean isNearZero() {
|
||||
var s = 1e-8;
|
||||
return Math.abs(x) < s && Math.abs(y) < s && Math.abs(z) < s;
|
||||
}
|
||||
|
||||
public @NotNull Vec3 unit() {
|
||||
return div(length());
|
||||
|
@ -137,12 +137,11 @@ public final class Camera {
|
||||
|
||||
var optional = scene.hit(ray, new Range(0.001, Double.POSITIVE_INFINITY));
|
||||
if (optional.isPresent()) {
|
||||
var result = optional.get();
|
||||
|
||||
var newDirection = result.normal().plus(Vec3.random(true));
|
||||
var scattered = new Ray(result.position(), newDirection);
|
||||
|
||||
return Color.lerp(Color.BLACK, getColor(scene, scattered, depth - 1), 0.5);
|
||||
var hit = optional.get();
|
||||
var material = hit.material();
|
||||
return material.scatter(ray, hit)
|
||||
.map(scatter -> Color.multiply(scatter.attenuation(), getColor(scene, scatter.ray(), depth - 1)))
|
||||
.orElse(Color.BLACK);
|
||||
} else {
|
||||
return getSkyboxColor(ray);
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ public record Color(double r, double g, double b) {
|
||||
public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
|
||||
public static final @NotNull Color SKY = new Color(0.5, 0.7, 1.0);
|
||||
public static final @NotNull Color RED = new Color(1.0, 0.0, 0.0);
|
||||
public static final @NotNull Color GREEN = new Color(0.0, 1.0, 0.0);
|
||||
public static final @NotNull Color BLUE = new Color(0.0, 0.0, 1.0);
|
||||
|
||||
public static @NotNull Color lerp(@NotNull Color a, @NotNull Color b, double t) {
|
||||
if (t < 0) return a;
|
||||
@ -18,6 +20,10 @@ public record Color(double r, double g, double b) {
|
||||
);
|
||||
}
|
||||
|
||||
public static @NotNull Color multiply(@NotNull Color a, @NotNull Color b) {
|
||||
return new Color(a.r() * b.r(), a.g() * b.g(), a.b() * b.b());
|
||||
}
|
||||
|
||||
public Color {
|
||||
if (r < 0 || r > 1 || g < 0 || g > 1 || b < 0 || b > 1) {
|
||||
throw new IllegalArgumentException("r, g and b must be in the range 0 to 1");
|
||||
|
@ -1,11 +1,17 @@
|
||||
package eu.jonahbauer.raytracing.scene;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.material.Material;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record HitResult(double t, @NotNull Vec3 position, @NotNull Vec3 normal) implements Comparable<HitResult> {
|
||||
public record HitResult(
|
||||
double t,
|
||||
@NotNull Vec3 position,
|
||||
@NotNull Vec3 normal,
|
||||
@NotNull Material material
|
||||
) implements Comparable<HitResult> {
|
||||
public HitResult {
|
||||
if (t < 0 || !Double.isFinite(t)) throw new IllegalArgumentException("t must be non-negative");
|
||||
Objects.requireNonNull(position, "position");
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.jonahbauer.raytracing.scene;
|
||||
|
||||
import eu.jonahbauer.raytracing.material.Material;
|
||||
import eu.jonahbauer.raytracing.math.Range;
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
@ -8,16 +9,16 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public record Sphere(@NotNull Vec3 center, double radius) implements Hittable {
|
||||
public static final @NotNull Sphere UNIT = new Sphere(Vec3.ZERO, 1.0);
|
||||
public record Sphere(@NotNull Vec3 center, double radius, @NotNull Material material) implements Hittable {
|
||||
|
||||
public Sphere {
|
||||
Objects.requireNonNull(center, "center");
|
||||
Objects.requireNonNull(material, "material");
|
||||
if (radius <= 0 || !Double.isFinite(radius)) throw new IllegalArgumentException("radius must be positive");
|
||||
}
|
||||
|
||||
public Sphere(double x, double y, double z, double r) {
|
||||
this(new Vec3(x, y, z), r);
|
||||
public Sphere(double x, double y, double z, double r, @NotNull Material material) {
|
||||
this(new Vec3(x, y, z), r, material);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -38,11 +39,11 @@ public record Sphere(@NotNull Vec3 center, double radius) implements Hittable {
|
||||
if (!range.surrounds(t)) return Optional.empty();
|
||||
|
||||
var position = ray.at(t);
|
||||
return Optional.of(new HitResult(t, position, position.minus(center)));
|
||||
return Optional.of(new HitResult(t, position, position.minus(center), material));
|
||||
}
|
||||
|
||||
public @NotNull Sphere withCenter(@NotNull Vec3 center) {
|
||||
return new Sphere(center, radius);
|
||||
return new Sphere(center, radius, material);
|
||||
}
|
||||
|
||||
public @NotNull Sphere withCenter(double x, double y, double z) {
|
||||
@ -50,6 +51,6 @@ public record Sphere(@NotNull Vec3 center, double radius) implements Hittable {
|
||||
}
|
||||
|
||||
public @NotNull Sphere withRadius(double radius) {
|
||||
return new Sphere(center, radius);
|
||||
return new Sphere(center, radius, material);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user