rework interface between materials and rgb colors

feature/spectral
jbb01 5 months ago
parent 9eb8afcb59
commit b8aae8c2e5

@ -55,11 +55,11 @@ public class Examples {
var cs = ColorSpaces.sRGB; var cs = ColorSpaces.sRGB;
return new Example( return new Example(
new Scene(getSkyBox(), List.of( new Scene(getSkyBox(), List.of(
new Sphere(new Vec3(0, -100.5, -1.0), 100.0, new LambertianMaterial(new ColorRGB(0.8, 0.8, 0.0), cs)), new Sphere(new Vec3(0, -100.5, -1.0), 100.0, new LambertianMaterial(cs.albedo(0.8, 0.8, 0.0))),
new Sphere(new Vec3(0, 0, -1.2), 0.5, new LambertianMaterial(new ColorRGB(0.1, 0.2, 0.5), cs)), new Sphere(new Vec3(0, 0, -1.2), 0.5, new LambertianMaterial(cs.albedo(0.1, 0.2, 0.5))),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.5, new DielectricMaterial(1.5)), new Sphere(new Vec3(-1.0, 0, -1.2), 0.5, new DielectricMaterial(1.5)),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.4, new DielectricMaterial(1 / 1.5)), new Sphere(new Vec3(-1.0, 0, -1.2), 0.4, new DielectricMaterial(1 / 1.5)),
new Sphere(new Vec3(1.0, 0, -1.2), 0.5, new MetallicMaterial(new ColorRGB(0.8, 0.6, 0.2), cs, 0.0)) new Sphere(new Vec3(1.0, 0, -1.2), 0.5, new MetallicMaterial(cs.albedo(0.8, 0.6, 0.2)))
)), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height * 16 / 9, height) .withImage(height * 16 / 9, height)
@ -92,12 +92,12 @@ public class Examples {
if (rnd < 0.8) { if (rnd < 0.8) {
// diffuse // diffuse
var albedo = ColorRGB.random(rng).times(ColorRGB.random(rng)); var albedo = ColorRGB.random(rng).times(ColorRGB.random(rng));
material = new LambertianMaterial(albedo, cs); material = new LambertianMaterial(cs.albedo(albedo));
} else if (rnd < 0.95) { } else if (rnd < 0.95) {
// metal // metal
var albedo = ColorRGB.random(rng, 0.5, 1.0); var albedo = ColorRGB.random(rng, 0.5, 1.0);
var fuzz = rng.nextDouble() * 0.5; var fuzz = rng.nextDouble() * 0.5;
material = new MetallicMaterial(albedo, cs, fuzz); material = new MetallicMaterial(cs.albedo(albedo), fuzz);
} else { } else {
// glass // glass
material = new DielectricMaterial(1.5); material = new DielectricMaterial(1.5);
@ -108,8 +108,8 @@ public class Examples {
} }
objects.add(new Sphere(new Vec3(0, 1, 0), 1.0, new DielectricMaterial(1.5))); objects.add(new Sphere(new Vec3(0, 1, 0), 1.0, new DielectricMaterial(1.5)));
objects.add(new Sphere(new Vec3(-4, 1, 0), 1.0, new LambertianMaterial(new ColorRGB(0.4, 0.2, 0.1), cs))); objects.add(new Sphere(new Vec3(-4, 1, 0), 1.0, new LambertianMaterial(cs.albedo(0.4, 0.2, 0.1))));
objects.add(new Sphere(new Vec3(4, 1, 0), 1.0, new MetallicMaterial(new ColorRGB(0.7, 0.6, 0.5), cs))); objects.add(new Sphere(new Vec3(4, 1, 0), 1.0, new MetallicMaterial(cs.albedo(0.7, 0.6, 0.5))));
var camera = SimpleCamera.builder() var camera = SimpleCamera.builder()
.withImage(height * 16 / 9, height) .withImage(height * 16 / 9, height)
@ -128,11 +128,11 @@ public class Examples {
var cs = ColorSpaces.sRGB; var cs = ColorSpaces.sRGB;
return new Example( return new Example(
new Scene(getSkyBox(), List.of( new Scene(getSkyBox(), List.of(
new Parallelogram(new Vec3(-3, -2, 5), new Vec3(0, 0, -4), new Vec3(0, 4, 0), new LambertianMaterial(new ColorRGB(1.0, 0.2, 0.2), cs)), new Parallelogram(new Vec3(-3, -2, 5), new Vec3(0, 0, -4), new Vec3(0, 4, 0), new LambertianMaterial(cs.albedo(1.0, 0.2, 0.2))),
new Parallelogram(new Vec3(-2, -2, 0), new Vec3(4, 0, 0), new Vec3(0, 4, 0), new LambertianMaterial(new ColorRGB(0.2, 1.0, 0.2), cs)), new Parallelogram(new Vec3(-2, -2, 0), new Vec3(4, 0, 0), new Vec3(0, 4, 0), new LambertianMaterial(cs.albedo(0.2, 1.0, 0.2))),
new Parallelogram(new Vec3(3, -2, 1), new Vec3(0, 0, 4), new Vec3(0, 4, 0), new LambertianMaterial(new ColorRGB(0.2, 0.2, 1.0), cs)), new Parallelogram(new Vec3(3, -2, 1), new Vec3(0, 0, 4), new Vec3(0, 4, 0), new LambertianMaterial(cs.albedo(0.2, 0.2, 1.0))),
new Parallelogram(new Vec3(-2, 3, 1), new Vec3(4, 0, 0), new Vec3(0, 0, 4), new LambertianMaterial(new ColorRGB(1.0, 0.5, 0.0), cs)), new Parallelogram(new Vec3(-2, 3, 1), new Vec3(4, 0, 0), new Vec3(0, 0, 4), new LambertianMaterial(cs.albedo(1.0, 0.5, 0.0))),
new Parallelogram(new Vec3(-2, -3, 5), new Vec3(4, 0, 0), new Vec3(0, 0, -4), new LambertianMaterial(new ColorRGB(0.2, 0.8, 0.8), cs)) new Parallelogram(new Vec3(-2, -3, 5), new Vec3(4, 0, 0), new Vec3(0, 0, -4), new LambertianMaterial(cs.albedo(0.2, 0.8, 0.8)))
)), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height, height) .withImage(height, height)
@ -148,10 +148,10 @@ public class Examples {
var cs = ColorSpaces.sRGB; var cs = ColorSpaces.sRGB;
return new Example( return new Example(
new Scene(List.of( new Scene(List.of(
new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new ColorRGB(0.2, 0.2, 0.2), cs)), new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(cs.albedo(0.2, 0.2, 0.2))),
new Sphere(new Vec3(0, 2, 0), 2, new LambertianMaterial(new ColorRGB(0.2, 0.2, 0.2), cs)), new Sphere(new Vec3(0, 2, 0), 2, new LambertianMaterial(cs.albedo(0.2, 0.2, 0.2))),
new Parallelogram(new Vec3(3, 1, -2), new Vec3(2, 0, 0), new Vec3(0, 2, 0), new DiffuseLight(new ColorRGB(4.0, 4.0, 4.0), cs)), new Parallelogram(new Vec3(3, 1, -2), new Vec3(2, 0, 0), new Vec3(0, 2, 0), new DiffuseLight(cs.illuminant(4.0))),
new Sphere(new Vec3(0, 7, 0), 2, new DiffuseLight(new ColorRGB(4.0, 4.0, 4.0), cs)) new Sphere(new Vec3(0, 7, 0), 2, new DiffuseLight(cs.illuminant(4.0)))
)), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height * 16 / 9, height) .withImage(height * 16 / 9, height)
@ -166,10 +166,10 @@ public class Examples {
if (height <= 0) height = 600; if (height <= 0) height = 600;
var cs = ColorSpaces.sRGB; var cs = ColorSpaces.sRGB;
var red = new LambertianMaterial(new ColorRGB(.65, .05, .05), cs); var red = new LambertianMaterial(cs.albedo(.65, .05, .05));
var white = new LambertianMaterial(new ColorRGB(.73, .73, .73), cs); var white = new LambertianMaterial(cs.albedo(.73, .73, .73));
var green = new LambertianMaterial(new ColorRGB(.12, .45, .15), cs); var green = new LambertianMaterial(cs.albedo(.12, .45, .15));
var light = new DiffuseLight(new ColorRGB(15.0, 15.0, 15.0), cs); var light = new DiffuseLight(cs.illuminant(15.0));
return new Example( return new Example(
new Scene(List.of( new Scene(List.of(
@ -194,10 +194,10 @@ public class Examples {
public static @NotNull Example getCornellBoxSmoke(int height) { public static @NotNull Example getCornellBoxSmoke(int height) {
if (height <= 0) height = 600; if (height <= 0) height = 600;
var cs = ColorSpaces.sRGB; var cs = ColorSpaces.sRGB;
var red = new LambertianMaterial(new ColorRGB(.65, .05, .05), cs); var red = new LambertianMaterial(cs.albedo(.65, .05, .05));
var white = new LambertianMaterial(new ColorRGB(.73, .73, .73), cs); var white = new LambertianMaterial(cs.albedo(.73, .73, .73));
var green = new LambertianMaterial(new ColorRGB(.12, .45, .15), cs); var green = new LambertianMaterial(cs.albedo(.12, .45, .15));
var light = new DiffuseLight(new ColorRGB(15.0, 15.0, 15.0), cs); var light = new DiffuseLight(cs.illuminant(15.0));
return new Example( return new Example(
new Scene(List.of( new Scene(List.of(
@ -207,13 +207,13 @@ public class Examples {
new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white) new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white)
.rotateY(Math.toRadians(15)) .rotateY(Math.toRadians(15))
.translate(new Vec3(265, 0, 295)), .translate(new Vec3(265, 0, 295)),
0.01, new IsotropicMaterial(ColorRGB.BLACK, cs) 0.01, new IsotropicMaterial(cs.albedo(ColorRGB.BLACK))
), ),
new ConstantMedium( new ConstantMedium(
new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white) new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white)
.rotateY(Math.toRadians(-18)) .rotateY(Math.toRadians(-18))
.translate(new Vec3(130, 0, 65)), .translate(new Vec3(130, 0, 65)),
0.01, new IsotropicMaterial(ColorRGB.WHITE, cs) 0.01, new IsotropicMaterial(cs.albedo(ColorRGB.WHITE))
) )
)), )),
SimpleCamera.builder() SimpleCamera.builder()
@ -229,10 +229,10 @@ public class Examples {
if (height <= 0) height = 600; if (height <= 0) height = 600;
var cs = ColorSpaces.sRGB; var cs = ColorSpaces.sRGB;
var red = new LambertianMaterial(new ColorRGB(.65, .05, .05), cs); var red = new LambertianMaterial(cs.albedo(.65, .05, .05));
var white = new LambertianMaterial(new ColorRGB(.73, .73, .73), cs); var white = new LambertianMaterial(cs.albedo(.73, .73, .73));
var green = new LambertianMaterial(new ColorRGB(.12, .45, .15), cs); var green = new LambertianMaterial(cs.albedo(.12, .45, .15));
var light = new DiffuseLight(new ColorRGB(7.0, 7.0, 7.0), cs); var light = new DiffuseLight(cs.illuminant(7.0));
var glass = new DielectricMaterial(1.5); var glass = new DielectricMaterial(1.5);
var room = new Box(new Vec3(0, 0, 0), new Vec3(555, 555, 555), white, white, red, green, white, null); var room = new Box(new Vec3(0, 0, 0), new Vec3(555, 555, 555), white, white, red, green, white, null);
@ -259,15 +259,15 @@ public class Examples {
var cs = ColorSpaces.sRGB; var cs = ColorSpaces.sRGB;
record Partei(String name, ColorRGB color, double stimmen) { } record Partei(String name, ColorRGB color, double stimmen) { }
var data = List.of( var data = List.of(
new Partei("CDU", new ColorRGB(0x00, 0x4B, 0x76), 18.9), new Partei("CDU", new ColorRGB(0x004B76), 18.9),
new Partei("SPD", new ColorRGB(0xC0, 0x00, 0x3C), 25.7), new Partei("SPD", new ColorRGB(0xC0003C), 25.7),
new Partei("AfD", new ColorRGB(0x80, 0xCD, 0xEC), 10.3), new Partei("AfD", new ColorRGB(0x80CDEC), 10.3),
new Partei("FDP", new ColorRGB(0xF7, 0xBB, 0x3D), 11.5), new Partei("FDP", new ColorRGB(0xF7BB3D), 11.5),
new Partei("DIE LINKE", new ColorRGB(0x5F, 0x31, 0x6E), 4.9), new Partei("DIE LINKE", new ColorRGB(0x5F316E), 4.9),
new Partei("GRÜNE", new ColorRGB(0x00, 0x85, 0x4A), 14.8), new Partei("GRÜNE", new ColorRGB(0x00854A), 14.8),
new Partei("CSU", new ColorRGB(0x00, 0x77, 0xB6), 5.2) new Partei("CSU", new ColorRGB(0x0077B6), 5.2)
); );
var white = new LambertianMaterial(new ColorRGB(.99, .99, .99), cs); var white = new LambertianMaterial(cs.albedo(.99, .99, .99));
var count = data.size(); var count = data.size();
var size = 75d; var size = 75d;
@ -287,7 +287,7 @@ public class Examples {
objects.add(new Box( objects.add(new Box(
new Vec3((i + 1) * spacing + i * size, 0, spacing), new Vec3((i + 1) * spacing + i * size, 0, spacing),
new Vec3((i + 1) * spacing + (i + 1) * size, partei.stimmen() * 15, spacing + size), new Vec3((i + 1) * spacing + (i + 1) * size, partei.stimmen() * 15, spacing + size),
new DielectricMaterial(1.5, partei.color(), cs) new DielectricMaterial(1.5, cs.albedo(partei.color()))
)); ));
} }
@ -346,7 +346,7 @@ public class Examples {
// boxes // boxes
var boxes = new ArrayList<Hittable>(); var boxes = new ArrayList<Hittable>();
var ground = new LambertianMaterial(new ColorRGB(0.48, 0.83, 0.53), cs); var ground = new LambertianMaterial(cs.albedo(0.48, 0.83, 0.53));
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) { for (int j = 0; j < 20; j++) {
var w = 100.0; var w = 100.0;
@ -364,23 +364,23 @@ public class Examples {
// light // light
objects.add(new Parallelogram( objects.add(new Parallelogram(
new Vec3(123, 554, 147), new Vec3(300, 0, 0), new Vec3(0, 0, 265), new Vec3(123, 554, 147), new Vec3(300, 0, 0), new Vec3(0, 0, 265),
new DiffuseLight(new ColorRGB(7., 7., 7.), cs) new DiffuseLight(cs.illuminant(7.0))
)); ));
// spheres with different materials // spheres with different materials
objects.add(new Sphere(new Vec3(400, 400, 200), 50, new LambertianMaterial(new ColorRGB(0.7, 0.3, 0.1), cs))); objects.add(new Sphere(new Vec3(400, 400, 200), 50, new LambertianMaterial(cs.albedo(0.7, 0.3, 0.1))));
objects.add(new Sphere(new Vec3(260, 150, 45), 50, new DielectricMaterial(1.5))); objects.add(new Sphere(new Vec3(260, 150, 45), 50, new DielectricMaterial(1.5)));
objects.add(new Sphere(new Vec3(0, 150, 145), 50, new MetallicMaterial(new ColorRGB(0.8, 0.8, 0.9), cs, 1.0))); objects.add(new Sphere(new Vec3(0, 150, 145), 50, new MetallicMaterial(cs.albedo(0.8, 0.8, 0.9), 1.0)));
// glass sphere filled with gas // glass sphere filled with gas
var boundary = new Sphere(new Vec3(360, 150, 145), 70, new DielectricMaterial(1.5)); var boundary = new Sphere(new Vec3(360, 150, 145), 70, new DielectricMaterial(1.5));
objects.add(boundary); objects.add(boundary);
objects.add(new ConstantMedium(boundary, 0.2, new IsotropicMaterial(new ColorRGB(0.2, 0.4, 0.9), cs))); objects.add(new ConstantMedium(boundary, 0.2, new IsotropicMaterial(cs.albedo(0.2, 0.4, 0.9))));
// put the world in a glass sphere // put the world in a glass sphere
objects.add(new ConstantMedium( objects.add(new ConstantMedium(
new Sphere(new Vec3(0, 0, 0), 5000, new DielectricMaterial(1.5)), new Sphere(new Vec3(0, 0, 0), 5000, new DielectricMaterial(1.5)),
0.0001, new IsotropicMaterial(new ColorRGB(1., 1., 1.), cs) 0.0001, new IsotropicMaterial(cs.albedo(1.0, 1.0, 1.0))
)); ));
// textures spheres // textures spheres
@ -388,7 +388,7 @@ public class Examples {
objects.add(new Sphere(new Vec3(220, 280, 300), 80, new LambertianMaterial(new PerlinTexture(0.2)))); objects.add(new Sphere(new Vec3(220, 280, 300), 80, new LambertianMaterial(new PerlinTexture(0.2))));
// box from spheres // box from spheres
var white = new LambertianMaterial(new ColorRGB(.73, .73, .73), cs); var white = new LambertianMaterial(cs.albedo(.73, .73, .73));
var spheres = new ArrayList<Hittable>(); var spheres = new ArrayList<Hittable>();
for (int j = 0; j < 1000; j++) { for (int j = 0; j < 1000; j++) {
spheres.add(new Sphere(new Vec3(random.nextDouble(165), random.nextDouble(165), random.nextDouble(165)), 10, white)); spheres.add(new Sphere(new Vec3(random.nextDouble(165), random.nextDouble(165), random.nextDouble(165)), 10, white));

@ -23,11 +23,7 @@ public record ColorRGB(double r, double g, double b) implements IVec3<ColorRGB>
} }
public ColorRGB(int rgb) { public ColorRGB(int rgb) {
this((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); this(((rgb >> 16) & 0xFF) / 255d, ((rgb >> 8) & 0xFF) / 255d, (rgb & 0xFF) / 255d);
}
public ColorRGB(int red, int green, int blue) {
this(red / 255f, green / 255f, blue / 255f);
} }
public ColorRGB { public ColorRGB {

@ -2,8 +2,7 @@ package eu.jonahbauer.raytracing.render.color;
import eu.jonahbauer.raytracing.math.Matrix3; import eu.jonahbauer.raytracing.math.Matrix3;
import eu.jonahbauer.raytracing.math.Vec3; import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.render.spectrum.DenselySampledSpectrum; import eu.jonahbauer.raytracing.render.spectrum.*;
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Objects; import java.util.Objects;
@ -85,7 +84,7 @@ public final class ColorSpace {
} }
} }
public @NotNull SigmoidPolynomial toSpectrum(@NotNull ColorRGB rgb) { public @NotNull SigmoidPolynomial toPolynomial(@NotNull ColorRGB rgb) {
return RGBtoSpectrumTable.get(new ColorRGB( return RGBtoSpectrumTable.get(new ColorRGB(
Math.max(0, rgb.r()), Math.max(0, rgb.r()),
Math.max(0, rgb.g()), Math.max(0, rgb.g()),
@ -93,6 +92,38 @@ public final class ColorSpace {
)); ));
} }
public @NotNull Spectrum toSpectrum(@NotNull ColorRGB rgb, @NotNull Spectrum.Type type) {
return switch (type) {
case ALBEDO -> new RGBAlbedoSpectrum(this, rgb);
case ILLUMINANT -> new RGBIlluminantSpectrum(this, rgb);
case UNBOUNDED -> new RGBUnboundedSpectrum(this, rgb);
};
}
public @NotNull Spectrum albedo(double r, double g, double b) {
return albedo(new ColorRGB(r, g, b));
}
public @NotNull Spectrum albedo(@NotNull ColorRGB rgb) {
return toSpectrum(rgb, Spectrum.Type.ALBEDO);
}
public @NotNull Spectrum illuminant(double intensity) {
return illuminant.scale(intensity);
}
public @NotNull Spectrum illuminant(double r, double g, double b) {
return illuminant(new ColorRGB(r, g, b));
}
public @NotNull Spectrum illuminant(@NotNull ColorRGB rgb) {
return toSpectrum(rgb, Spectrum.Type.ILLUMINANT);
}
/*
* Accessors
*/
public @NotNull Chromaticity r() { public @NotNull Chromaticity r() {
return r; return r;
} }

@ -2,9 +2,6 @@ package eu.jonahbauer.raytracing.render.material;
import eu.jonahbauer.raytracing.math.Ray; import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.math.Vec3; import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.render.color.ColorRGB;
import eu.jonahbauer.raytracing.render.color.ColorSpace;
import eu.jonahbauer.raytracing.render.spectrum.RGBAlbedoSpectrum;
import eu.jonahbauer.raytracing.render.spectrum.Spectra; import eu.jonahbauer.raytracing.render.spectrum.Spectra;
import eu.jonahbauer.raytracing.render.texture.Texture; import eu.jonahbauer.raytracing.render.texture.Texture;
import eu.jonahbauer.raytracing.scene.HitResult; import eu.jonahbauer.raytracing.scene.HitResult;
@ -14,25 +11,34 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.random.RandomGenerator; import java.util.random.RandomGenerator;
public record DielectricMaterial(double refractionIndex, @NotNull Texture texture) implements Material { public record DielectricMaterial(@NotNull RefractiveIndex ri, @NotNull Texture texture) implements Material {
public DielectricMaterial(double refractionIndex) {
this(refractionIndex, Spectra.WHITE);
}
public DielectricMaterial { public DielectricMaterial {
Objects.requireNonNull(ri, "ri");
Objects.requireNonNull(texture, "texture"); Objects.requireNonNull(texture, "texture");
} }
public DielectricMaterial(double refractionIndex, @NotNull ColorRGB color, @NotNull ColorSpace cs) { public DielectricMaterial(@NotNull RefractiveIndex ri) {
this(refractionIndex, new RGBAlbedoSpectrum(cs, color)); this(ri, Spectra.WHITE);
}
public DielectricMaterial(double ri) {
this(new ConstantRefractiveIndex(ri), Spectra.WHITE);
}
public DielectricMaterial(double ri, @NotNull Texture texture) {
this(new ConstantRefractiveIndex(ri), texture);
} }
@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.isFrontFace() ? (1 / refractionIndex) : refractionIndex; var ri = switch (this.ri) {
case ConstantRefractiveIndex(var x) -> x;
case RefractiveIndex x -> x.get(ray.lambda().collapse());
};
if (hit.isFrontFace()) ri = 1 / ri;
var cosTheta = Math.min(- ray.direction().unit().dot(hit.normal()), 1.0); var cosTheta = Math.min(- ray.direction().unit().dot(hit.normal()), 1.0);
var reflectance = reflectance(cosTheta); var reflectance = reflectance(cosTheta, ri);
var reflect = reflectance > random.nextDouble(); var reflect = reflectance > random.nextDouble();
var newDirection = (reflect ? Optional.<Vec3>empty() : Vec3.refract(ray.direction(), hit.normal(), ri)) var newDirection = (reflect ? Optional.<Vec3>empty() : Vec3.refract(ray.direction(), hit.normal(), ri))
@ -42,10 +48,38 @@ public record DielectricMaterial(double refractionIndex, @NotNull Texture textur
return Optional.of(new SpecularScatterResult(attenuation, new Ray(hit.position(), newDirection, ray.lambda()))); return Optional.of(new SpecularScatterResult(attenuation, new Ray(hit.position(), newDirection, ray.lambda())));
} }
private double reflectance(double cos) { private double reflectance(double cos, double ri) {
// use schlick's approximation for reflectance // use schlick's approximation for reflectance
var r0 = (1 - refractionIndex) / (1 + refractionIndex); var r0 = (1 - ri) / (1 + ri);
r0 = r0 * r0; r0 = r0 * r0;
return r0 + (1 - r0) * (1 - cos) * (1 - cos) * (1 - cos) * (1 - cos) * (1 - cos); return r0 + (1 - r0) * (1 - cos) * (1 - cos) * (1 - cos) * (1 - cos) * (1 - cos);
} }
@FunctionalInterface
public interface RefractiveIndex {
double get(double lambda);
}
public record ConstantRefractiveIndex(double ri) implements RefractiveIndex {
@Override
public double get(double lambda) {
return ri;
}
}
public record SellmeierRefractiveIndex(
double B1, double B2, double B3,
double C1, double C2, double C3
) implements RefractiveIndex {
@Override
public double get(double lambda) {
var l2 = lambda * lambda * 1E-6; // square and convert to µm
var x = 1 + B1 * l2 / (l2 - C1)
+ B2 * l2 / (l2 - C2)
+ B3 * l2 / (l2 - C3);
return Math.sqrt(x);
}
}
} }

@ -1,9 +1,6 @@
package eu.jonahbauer.raytracing.render.material; package eu.jonahbauer.raytracing.render.material;
import eu.jonahbauer.raytracing.math.Ray; import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.render.color.ColorRGB;
import eu.jonahbauer.raytracing.render.color.ColorSpace;
import eu.jonahbauer.raytracing.render.spectrum.RGBIlluminantSpectrum;
import eu.jonahbauer.raytracing.render.spectrum.Spectrum; import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
import eu.jonahbauer.raytracing.render.texture.Texture; import eu.jonahbauer.raytracing.render.texture.Texture;
import eu.jonahbauer.raytracing.scene.HitResult; import eu.jonahbauer.raytracing.scene.HitResult;
@ -18,10 +15,6 @@ public record DiffuseLight(@NotNull Texture texture) implements Material {
Objects.requireNonNull(texture, "texture"); Objects.requireNonNull(texture, "texture");
} }
public DiffuseLight(@NotNull ColorRGB color, @NotNull ColorSpace cs) {
this(new RGBIlluminantSpectrum(cs, color));
}
@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) {
return Optional.empty(); return Optional.empty();

@ -2,9 +2,6 @@ package eu.jonahbauer.raytracing.render.material;
import eu.jonahbauer.raytracing.math.Ray; import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.render.renderer.pdf.SphereProbabilityDensityFunction; import eu.jonahbauer.raytracing.render.renderer.pdf.SphereProbabilityDensityFunction;
import eu.jonahbauer.raytracing.render.color.ColorRGB;
import eu.jonahbauer.raytracing.render.color.ColorSpace;
import eu.jonahbauer.raytracing.render.spectrum.RGBAlbedoSpectrum;
import eu.jonahbauer.raytracing.render.spectrum.Spectrum; import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
import eu.jonahbauer.raytracing.render.texture.Texture; import eu.jonahbauer.raytracing.render.texture.Texture;
import eu.jonahbauer.raytracing.scene.HitResult; import eu.jonahbauer.raytracing.scene.HitResult;
@ -19,10 +16,6 @@ public record IsotropicMaterial(@NotNull Spectrum albedo) implements Material {
Objects.requireNonNull(albedo, "albedo"); Objects.requireNonNull(albedo, "albedo");
} }
public IsotropicMaterial(@NotNull ColorRGB color, @NotNull ColorSpace cs) {
this(new RGBAlbedoSpectrum(cs, color));
}
@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) {
return Optional.of(new PdfScatterResult(albedo(), new SphereProbabilityDensityFunction())); return Optional.of(new PdfScatterResult(albedo(), new SphereProbabilityDensityFunction()));

@ -2,9 +2,6 @@ package eu.jonahbauer.raytracing.render.material;
import eu.jonahbauer.raytracing.math.Ray; import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.render.renderer.pdf.CosineProbabilityDensityFunction; import eu.jonahbauer.raytracing.render.renderer.pdf.CosineProbabilityDensityFunction;
import eu.jonahbauer.raytracing.render.color.ColorRGB;
import eu.jonahbauer.raytracing.render.color.ColorSpace;
import eu.jonahbauer.raytracing.render.spectrum.RGBAlbedoSpectrum;
import eu.jonahbauer.raytracing.render.texture.Texture; import eu.jonahbauer.raytracing.render.texture.Texture;
import eu.jonahbauer.raytracing.scene.HitResult; import eu.jonahbauer.raytracing.scene.HitResult;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -18,10 +15,6 @@ public record LambertianMaterial(@NotNull Texture texture) implements Material {
Objects.requireNonNull(texture, "texture"); Objects.requireNonNull(texture, "texture");
} }
public LambertianMaterial(@NotNull ColorRGB color, @NotNull ColorSpace cs) {
this(new RGBAlbedoSpectrum(cs, color));
}
@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 attenuation = texture.get(hit); var attenuation = texture.get(hit);

@ -0,0 +1,25 @@
package eu.jonahbauer.raytracing.render.material;
import eu.jonahbauer.raytracing.render.color.ColorSpaces;
import eu.jonahbauer.raytracing.render.material.DielectricMaterial.SellmeierRefractiveIndex;
import eu.jonahbauer.raytracing.render.spectrum.Spectra;
import eu.jonahbauer.raytracing.render.texture.CheckerTexture;
import org.jetbrains.annotations.NotNull;
public final class Materials {
public static final @NotNull Material GLASS = new DielectricMaterial(new SellmeierRefractiveIndex(
1.0361212, 0.231792344, 1.01046945,
6.00069867E-3, 2.00179144E-2, 1.03560653E2
));
public static final @NotNull Material MIRROR = new MetallicMaterial(ColorSpaces.sRGB.albedo(0.7, 0.7, 0.7));
public static final @NotNull Material DEBUG = new DirectionalMaterial(
new LambertianMaterial(new CheckerTexture(50.0, ColorSpaces.sRGB.albedo(1.0, 0.0, 1.0), Spectra.BLACK)),
new LambertianMaterial(new CheckerTexture(50.0, ColorSpaces.sRGB.albedo(1.0, 1.0, 1.0), Spectra.BLACK))
);
private Materials() {
throw new UnsupportedOperationException();
}
}

@ -2,9 +2,6 @@ package eu.jonahbauer.raytracing.render.material;
import eu.jonahbauer.raytracing.math.Ray; import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.math.Vec3; import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.render.color.ColorRGB;
import eu.jonahbauer.raytracing.render.color.ColorSpace;
import eu.jonahbauer.raytracing.render.spectrum.RGBAlbedoSpectrum;
import eu.jonahbauer.raytracing.render.texture.Texture; import eu.jonahbauer.raytracing.render.texture.Texture;
import eu.jonahbauer.raytracing.scene.HitResult; import eu.jonahbauer.raytracing.scene.HitResult;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -19,14 +16,6 @@ public record MetallicMaterial(@NotNull Texture texture, double fuzz) implements
this(texture, 0); this(texture, 0);
} }
public MetallicMaterial(@NotNull ColorRGB color, @NotNull ColorSpace cs) {
this(color, cs, 0);
}
public MetallicMaterial(@NotNull ColorRGB color, @NotNull ColorSpace cs, double fuzz) {
this(new RGBAlbedoSpectrum(cs, color), fuzz);
}
public MetallicMaterial { public MetallicMaterial {
Objects.requireNonNull(texture, "texture"); Objects.requireNonNull(texture, "texture");
if (fuzz < 0 || !Double.isFinite(fuzz)) throw new IllegalArgumentException("fuzz must be non-negative"); if (fuzz < 0 || !Double.isFinite(fuzz)) throw new IllegalArgumentException("fuzz must be non-negative");

@ -12,7 +12,7 @@ public final class RGBAlbedoSpectrum implements Spectrum {
if (rgb.r() < 0 || rgb.r() > 1 || rgb.g() < 0 || rgb.g() > 1 || rgb.b() < 0 || rgb.b() > 1) { if (rgb.r() < 0 || rgb.r() > 1 || rgb.g() < 0 || rgb.g() > 1 || rgb.b() < 0 || rgb.b() > 1) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
this.polynomial = cs.toSpectrum(rgb); this.polynomial = cs.toPolynomial(rgb);
} }
@Override @Override

@ -20,7 +20,7 @@ public final class RGBIlluminantSpectrum implements Spectrum {
} }
var max = Math.max(rgb.r(), Math.max(rgb.g(), rgb.b())); var max = Math.max(rgb.r(), Math.max(rgb.g(), rgb.b()));
this.scale = 2 * max; this.scale = 2 * max;
this.polynomial = cs.toSpectrum(scale != 0 ? rgb.div(scale) : ColorRGB.BLACK); this.polynomial = cs.toPolynomial(scale != 0 ? rgb.div(scale) : ColorRGB.BLACK);
this.illuminant = cs.illuminant(); this.illuminant = cs.illuminant();
} }

@ -15,7 +15,7 @@ public final class RGBUnboundedSpectrum implements Spectrum {
} }
var max = Math.max(rgb.r(), Math.max(rgb.g(), rgb.b())); var max = Math.max(rgb.r(), Math.max(rgb.g(), rgb.b()));
this.scale = 2 * max; this.scale = 2 * max;
this.polynomial = cs.toSpectrum(scale != 0 ? rgb.div(scale) : ColorRGB.BLACK); this.polynomial = cs.toPolynomial(scale != 0 ? rgb.div(scale) : ColorRGB.BLACK);
} }
@Override @Override

@ -72,4 +72,8 @@ public interface Spectrum extends Texture, SkyBox {
default @NotNull SampledSpectrum getColor(@NotNull Ray ray) { default @NotNull SampledSpectrum getColor(@NotNull Ray ray) {
return this.sample(ray.lambda()); return this.sample(ray.lambda());
} }
enum Type {
ALBEDO, ILLUMINANT, UNBOUNDED
}
} }

@ -3,8 +3,6 @@ package eu.jonahbauer.raytracing.render.texture;
import eu.jonahbauer.raytracing.math.Vec3; import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.render.color.ColorRGB; import eu.jonahbauer.raytracing.render.color.ColorRGB;
import eu.jonahbauer.raytracing.render.color.ColorSpace; import eu.jonahbauer.raytracing.render.color.ColorSpace;
import eu.jonahbauer.raytracing.render.spectrum.RGBAlbedoSpectrum;
import eu.jonahbauer.raytracing.render.spectrum.RGBIlluminantSpectrum;
import eu.jonahbauer.raytracing.render.spectrum.Spectrum; import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -21,7 +19,7 @@ public final class ImageTexture implements Texture {
private final int height; private final int height;
private final @NotNull Spectrum[][] spectra; private final @NotNull Spectrum[][] spectra;
public ImageTexture(@NotNull BufferedImage image, @NotNull ColorSpace cs, @NotNull Type type, boolean gamma) { public ImageTexture(@NotNull BufferedImage image, @NotNull ColorSpace cs, @NotNull Spectrum.Type type, boolean gamma) {
this.width = image.getWidth(); this.width = image.getWidth();
this.height = image.getHeight(); this.height = image.getHeight();
this.spectra = new Spectrum[height][width]; this.spectra = new Spectrum[height][width];
@ -30,13 +28,13 @@ public final class ImageTexture implements Texture {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
var rgb = new ColorRGB(image.getRGB(x, y)); var rgb = new ColorRGB(image.getRGB(x, y));
if (gamma) rgb = ColorRGB.inverseGamma(rgb); if (gamma) rgb = ColorRGB.inverseGamma(rgb);
spectra[y][x] = type.newSpectrum(cs, rgb); spectra[y][x] = cs.toSpectrum(rgb, type);
} }
} }
} }
public ImageTexture(@NotNull String path, @NotNull ColorSpace cs) { public ImageTexture(@NotNull String path, @NotNull ColorSpace cs) {
this(read(path), cs, Type.ALBEDO, true); this(read(path), cs, Spectrum.Type.ALBEDO, true);
} }
private static @NotNull BufferedImage read(@NotNull String path) { private static @NotNull BufferedImage read(@NotNull String path) {
@ -55,22 +53,4 @@ public final class ImageTexture implements Texture {
int y = (int) (v * (height - 1)); int y = (int) (v * (height - 1));
return spectra[y][x]; return spectra[y][x];
} }
public enum Type {
ALBEDO {
@Override
protected @NotNull Spectrum newSpectrum(@NotNull ColorSpace cs, @NotNull ColorRGB rgb) {
return new RGBAlbedoSpectrum(cs, rgb);
}
},
ILLUMINANT {
@Override
protected @NotNull Spectrum newSpectrum(@NotNull ColorSpace cs, @NotNull ColorRGB rgb) {
return new RGBIlluminantSpectrum(cs, rgb);
}
},
;
protected abstract @NotNull Spectrum newSpectrum(@NotNull ColorSpace cs, @NotNull ColorRGB rgb);
}
} }

Loading…
Cancel
Save