add texture support
This commit is contained in:
parent
2c28b10a6e
commit
7c0bc68ab2
@ -1,7 +1,8 @@
|
||||
package eu.jonahbauer.raytracing;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.CheckerTexture;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import eu.jonahbauer.raytracing.render.camera.SimpleCamera;
|
||||
import eu.jonahbauer.raytracing.render.material.*;
|
||||
import eu.jonahbauer.raytracing.scene.Hittable;
|
||||
@ -61,7 +62,10 @@ public class Examples {
|
||||
|
||||
var rng = new Random(1);
|
||||
var objects = new ArrayList<Hittable>();
|
||||
objects.add(new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new Color(0.5, 0.5, 0.5))));
|
||||
objects.add(new Sphere(
|
||||
new Vec3(0, -1000, 0), 1000,
|
||||
new LambertianMaterial(new CheckerTexture(0.32, new Color(.2, .3, .1), new Color(.9, .9, .9)))
|
||||
));
|
||||
|
||||
for (int a = -11; a < 11; a++) {
|
||||
for (int b = -11; b < 11; b++) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package eu.jonahbauer.raytracing.render.canvas;
|
||||
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package eu.jonahbauer.raytracing.render.canvas;
|
||||
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package eu.jonahbauer.raytracing.render.canvas;
|
||||
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
@ -2,7 +2,8 @@ package eu.jonahbauer.raytracing.render.material;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
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;
|
||||
|
||||
@ -10,13 +11,13 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
public record DielectricMaterial(double refractionIndex, @NotNull Color albedo) implements Material {
|
||||
public record DielectricMaterial(double refractionIndex, @NotNull Texture texture) implements Material {
|
||||
public DielectricMaterial(double refractionIndex) {
|
||||
this(refractionIndex, Color.WHITE);
|
||||
}
|
||||
|
||||
public DielectricMaterial {
|
||||
Objects.requireNonNull(albedo, "albedo");
|
||||
Objects.requireNonNull(texture, "texture");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -30,7 +31,8 @@ public record DielectricMaterial(double refractionIndex, @NotNull Color albedo)
|
||||
var newDirection = (reflect ? Optional.<Vec3>empty() : Vec3.refract(ray.direction(), hit.normal(), ri))
|
||||
.orElseGet(() -> Vec3.reflect(ray.direction(), hit.normal()));
|
||||
|
||||
return Optional.of(new ScatterResult(new Ray(hit.position(), newDirection), albedo));
|
||||
var attenuation = texture.get(hit);
|
||||
return Optional.of(new ScatterResult(new Ray(hit.position(), newDirection), attenuation));
|
||||
}
|
||||
|
||||
private double reflectance(double cos) {
|
||||
|
@ -1,14 +1,15 @@
|
||||
package eu.jonahbauer.raytracing.render.material;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
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 java.util.Optional;
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
public record DiffuseLight(@NotNull Color emit) implements Material {
|
||||
public record DiffuseLight(@NotNull Texture texture) implements Material {
|
||||
@Override
|
||||
public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit, @NotNull RandomGenerator random) {
|
||||
return Optional.empty();
|
||||
@ -16,6 +17,6 @@ public record DiffuseLight(@NotNull Color emit) implements Material {
|
||||
|
||||
@Override
|
||||
public @NotNull Color emitted(@NotNull HitResult hit) {
|
||||
return emit;
|
||||
return texture.get(hit);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package eu.jonahbauer.raytracing.render.material;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -2,7 +2,7 @@ package eu.jonahbauer.raytracing.render.material;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Texture;
|
||||
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -10,9 +10,9 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
public record LambertianMaterial(@NotNull Color albedo) implements Material {
|
||||
public record LambertianMaterial(@NotNull Texture texture) implements Material {
|
||||
public LambertianMaterial {
|
||||
Objects.requireNonNull(albedo, "albedo");
|
||||
Objects.requireNonNull(texture, "texture");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -20,7 +20,8 @@ public record LambertianMaterial(@NotNull Color albedo) implements Material {
|
||||
var newDirection = hit.normal().plus(Vec3.random(random, true));
|
||||
if (newDirection.isNearZero()) newDirection = hit.normal();
|
||||
|
||||
var attenuation = texture.get(hit);
|
||||
var scattered = new Ray(hit.position(), newDirection);
|
||||
return Optional.of(new ScatterResult(scattered, albedo));
|
||||
return Optional.of(new ScatterResult(scattered, attenuation));
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package eu.jonahbauer.raytracing.render.material;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -2,7 +2,7 @@ package eu.jonahbauer.raytracing.render.material;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Texture;
|
||||
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -10,14 +10,14 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
public record MetallicMaterial(@NotNull Color albedo, double fuzz) implements Material {
|
||||
public record MetallicMaterial(@NotNull Texture texture, double fuzz) implements Material {
|
||||
|
||||
public MetallicMaterial(@NotNull Color albedo) {
|
||||
this(albedo, 0);
|
||||
public MetallicMaterial(@NotNull Texture texture) {
|
||||
this(texture, 0);
|
||||
}
|
||||
|
||||
public MetallicMaterial {
|
||||
Objects.requireNonNull(albedo, "albedo");
|
||||
Objects.requireNonNull(texture, "texture");
|
||||
if (fuzz < 0 || !Double.isFinite(fuzz)) throw new IllegalArgumentException("fuzz must be non-negative");
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ public record MetallicMaterial(@NotNull Color albedo, double fuzz) implements Ma
|
||||
if (fuzz > 0) {
|
||||
newDirection = newDirection.unit().plus(Vec3.random(random, true).times(fuzz));
|
||||
}
|
||||
return Optional.of(new ScatterResult(new Ray(hit.position(), newDirection), albedo));
|
||||
var attenuation = texture.get(hit);
|
||||
return Optional.of(new ScatterResult(new Ray(hit.position(), newDirection), attenuation));
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package eu.jonahbauer.raytracing.render.renderer;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Range;
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import eu.jonahbauer.raytracing.render.camera.Camera;
|
||||
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
||||
import eu.jonahbauer.raytracing.scene.Scene;
|
||||
|
@ -0,0 +1,17 @@
|
||||
package eu.jonahbauer.raytracing.render.texture;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record CheckerTexture(double scale, @NotNull Texture even, @NotNull Texture odd) implements Texture {
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull Color get(double u, double v, @NotNull Vec3 p) {
|
||||
var x = (int) Math.floor(p.x() / scale);
|
||||
var y = (int) Math.floor(p.y() / scale);
|
||||
var z = (int) Math.floor(p.z() / scale);
|
||||
var even = (x + y + z) % 2 == 0;
|
||||
return even ? even().get(u, v, p) : odd().get(u, v, p);
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
package eu.jonahbauer.raytracing.render;
|
||||
package eu.jonahbauer.raytracing.render.texture;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public record Color(double r, double g, double b) {
|
||||
public record Color(double r, double g, double b) implements Texture {
|
||||
public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0);
|
||||
public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
|
||||
public static final @NotNull Color RED = new Color(1.0, 0.0, 0.0);
|
||||
@ -68,7 +69,9 @@ public record Color(double r, double g, double b) {
|
||||
}
|
||||
}
|
||||
|
||||
public Color {}
|
||||
public Color(int rgb) {
|
||||
this((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
|
||||
}
|
||||
|
||||
public Color(int red, int green, int blue) {
|
||||
this(red / 255f, green / 255f, blue / 255f);
|
||||
@ -86,7 +89,12 @@ public record Color(double r, double g, double b) {
|
||||
return toInt(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Color get(double u, double v, @NotNull Vec3 p) {
|
||||
return this;
|
||||
}
|
||||
|
||||
private static int toInt(double value) {
|
||||
return Math.max(0, Math.min(255, (int) (255.99 * value)));
|
||||
return Math.clamp((int) (255.99 * value), 0, 255);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package eu.jonahbauer.raytracing.render.texture;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface Texture {
|
||||
default @NotNull Color get(@NotNull HitResult hit) {
|
||||
return get(hit.u(), hit.v(), hit.position());
|
||||
}
|
||||
|
||||
@NotNull Color get(double u, double v, @NotNull Vec3 p);
|
||||
}
|
@ -7,17 +7,18 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
public record HitResult(
|
||||
double t,
|
||||
@NotNull Vec3 position,
|
||||
@NotNull Vec3 normal,
|
||||
@NotNull Material material,
|
||||
boolean frontFace
|
||||
double t, @NotNull Vec3 position, @NotNull Vec3 normal,
|
||||
@NotNull Material material, double u, double v, boolean frontFace
|
||||
) implements Comparable<HitResult> {
|
||||
public HitResult {
|
||||
Objects.requireNonNull(position, "position");
|
||||
normal = normal.unit();
|
||||
}
|
||||
|
||||
public @NotNull HitResult withPositionAndNormal(@NotNull Vec3 position, @NotNull Vec3 normal) {
|
||||
return new HitResult(t, position, normal, material, u, v, frontFace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull HitResult o) {
|
||||
return Double.compare(t, o.t);
|
||||
|
@ -2,7 +2,7 @@ package eu.jonahbauer.raytracing.scene;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.AABB;
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import eu.jonahbauer.raytracing.scene.util.HittableBinaryTree;
|
||||
import eu.jonahbauer.raytracing.scene.util.HittableCollection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package eu.jonahbauer.raytracing.scene;
|
||||
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
|
@ -51,7 +51,10 @@ public abstract class Hittable2D implements Hittable {
|
||||
if (!isInterior(alpha, beta)) return Optional.empty();
|
||||
|
||||
var frontFace = denominator < 0;
|
||||
return Optional.of(new HitResult(t, position, frontFace ? normal : normal.neg(), material, frontFace));
|
||||
return Optional.of(new HitResult(
|
||||
t, position, frontFace ? normal : normal.neg(),
|
||||
material, alpha, beta, frontFace
|
||||
));
|
||||
}
|
||||
|
||||
protected abstract boolean isInterior(double alpha, double beta);
|
||||
|
@ -32,7 +32,7 @@ public record ConstantMedium(@NotNull Hittable boundary, double density, @NotNul
|
||||
if (hitDistance > distance) return Optional.empty();
|
||||
|
||||
var t = tmin + hitDistance / length;
|
||||
return Optional.of(new HitResult(t, ray.at(t), Vec3.UNIT_X, material, true)); // arbitrary normal and frontFace
|
||||
return Optional.of(new HitResult(t, ray.at(t), Vec3.UNIT_X, material, 0, 0, true)); // arbitrary normal and frontFace
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,9 +49,19 @@ public final class Sphere implements Hittable {
|
||||
if (!range.surrounds(t)) return Optional.empty();
|
||||
|
||||
var position = ray.at(t);
|
||||
var normal = position.minus(center);
|
||||
var normal = position.minus(center).div(radius);
|
||||
var frontFace = normal.times(ray.direction()) < 0;
|
||||
return Optional.of(new HitResult(t, position, frontFace ? normal : normal.times(-1), material, frontFace));
|
||||
|
||||
var theta = Math.acos(- normal.y());
|
||||
var phi = Math.atan2(- normal.z(), normal.x()) + Math.PI;
|
||||
|
||||
var u = phi / (2 * Math.PI);
|
||||
var v = theta / Math.PI;
|
||||
|
||||
return Optional.of(new HitResult(
|
||||
t, position, frontFace ? normal : normal.neg(),
|
||||
material, u, v, frontFace
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,7 +79,7 @@ public final class RotateY extends Transform {
|
||||
-sin * normal.x() + cos * normal.z()
|
||||
);
|
||||
|
||||
return new HitResult(result.t(), newPosition, newNormal, result.material(), result.frontFace());
|
||||
return result.withPositionAndNormal(newPosition, newNormal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,13 +29,7 @@ public final class Translate extends Transform {
|
||||
|
||||
@Override
|
||||
protected @NotNull HitResult transform(@NotNull HitResult result) {
|
||||
return new HitResult(
|
||||
result.t(),
|
||||
result.position().plus(offset),
|
||||
result.normal(),
|
||||
result.material(),
|
||||
result.frontFace()
|
||||
);
|
||||
return result.withPositionAndNormal(result.position().plus(offset), result.normal());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,6 @@
|
||||
package eu.jonahbauer.raytracing.render.canvas;
|
||||
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import eu.jonahbauer.raytracing.render.ImageFormat;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
@ -3,7 +3,7 @@ package eu.jonahbauer.raytracing.scene.hittable3d;
|
||||
import eu.jonahbauer.raytracing.math.Range;
|
||||
import eu.jonahbauer.raytracing.math.Ray;
|
||||
import eu.jonahbauer.raytracing.math.Vec3;
|
||||
import eu.jonahbauer.raytracing.render.Color;
|
||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||
import eu.jonahbauer.raytracing.render.material.LambertianMaterial;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user