add texture support

main
jbb01 6 months ago
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,14 +2,14 @@ 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;
import java.util.Optional;
import java.util.random.RandomGenerator;
public record IsotropicMaterial(@NotNull Color albedo) implements Material{
public record IsotropicMaterial(@NotNull Color albedo) implements Material {
@Override
public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit, @NotNull RandomGenerator random) {
return Optional.of(new ScatterResult(new Ray(hit.position(), Vec3.random(random, true)), albedo()));

@ -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…
Cancel
Save