Compare commits
6 Commits
00fbf4e4f1
...
533461204a
Author | SHA1 | Date | |
---|---|---|---|
533461204a | |||
b8aae8c2e5 | |||
9eb8afcb59 | |||
75c56c0032 | |||
903ab1409b | |||
791ee606c4 |
@ -1,11 +1,11 @@
|
|||||||
package eu.jonahbauer.raytracing;
|
package eu.jonahbauer.raytracing;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpaces;
|
import eu.jonahbauer.raytracing.render.color.ColorSpaces;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBAlbedoSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.RGBAlbedoSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBIlluminantSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.RGBIlluminantSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.texture.CheckerTexture;
|
import eu.jonahbauer.raytracing.render.texture.CheckerTexture;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.camera.SimpleCamera;
|
import eu.jonahbauer.raytracing.render.camera.SimpleCamera;
|
||||||
import eu.jonahbauer.raytracing.render.material.*;
|
import eu.jonahbauer.raytracing.render.material.*;
|
||||||
import eu.jonahbauer.raytracing.render.texture.ImageTexture;
|
import eu.jonahbauer.raytracing.render.texture.ImageTexture;
|
||||||
@ -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));
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package eu.jonahbauer.raytracing;
|
package eu.jonahbauer.raytracing;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.ImageFormat;
|
|
||||||
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
||||||
import eu.jonahbauer.raytracing.render.canvas.LiveCanvas;
|
import eu.jonahbauer.raytracing.render.canvas.LiveCanvas;
|
||||||
import eu.jonahbauer.raytracing.render.canvas.XYZCanvas;
|
import eu.jonahbauer.raytracing.render.canvas.XYZCanvas;
|
||||||
|
import eu.jonahbauer.raytracing.render.image.PNGImageWriter;
|
||||||
import eu.jonahbauer.raytracing.render.renderer.SimpleRenderer;
|
import eu.jonahbauer.raytracing.render.renderer.SimpleRenderer;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorSpaces;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpaces;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBAlbedoSpectrum;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -44,7 +42,7 @@ public class Main {
|
|||||||
renderer.render(camera, scene, canvas);
|
renderer.render(camera, scene, canvas);
|
||||||
System.out.printf("rendering finished after %dms", (System.nanoTime() - time) / 1_000_000);
|
System.out.printf("rendering finished after %dms", (System.nanoTime() - time) / 1_000_000);
|
||||||
|
|
||||||
ImageFormat.PNG.write(canvas, config.path);
|
PNGImageWriter.sRGB.write(canvas, config.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Config(@NotNull Example example, @NotNull Path path, boolean preview, boolean iterative, boolean parallel, int samples, int depth) {
|
private record Config(@NotNull Example example, @NotNull Path path, boolean preview, boolean iterative, boolean parallel, int samples, int depth) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package eu.jonahbauer.raytracing.math;
|
package eu.jonahbauer.raytracing.math;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,15 +8,26 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @param <T> the type
|
* @param <T> the type
|
||||||
*/
|
*/
|
||||||
public interface IVec<T extends IVec<T>> {
|
public interface IVec<T extends IVec<T>> {
|
||||||
|
@Contract(pure = true)
|
||||||
double get(int i);
|
double get(int i);
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
@NotNull T plus(@NotNull T other);
|
@NotNull T plus(@NotNull T other);
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
@NotNull T minus(@NotNull T other);
|
@NotNull T minus(@NotNull T other);
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
@NotNull T times(@NotNull T other);
|
@NotNull T times(@NotNull T other);
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
@NotNull T times(double d);
|
@NotNull T times(double d);
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
default @NotNull T div(double d) {
|
default @NotNull T div(double d) {
|
||||||
return times(1 / d);
|
return times(1 / d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
double @NotNull[] toArray();
|
double @NotNull[] toArray();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package eu.jonahbauer.raytracing.math;
|
package eu.jonahbauer.raytracing.math;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,17 +8,23 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @param <T> the type
|
* @param <T> the type
|
||||||
*/
|
*/
|
||||||
public interface IVec3<T extends Record & IVec3<T>> extends IVec<T> {
|
public interface IVec3<T extends Record & IVec3<T>> extends IVec<T> {
|
||||||
|
@Contract(pure = true)
|
||||||
default double component1() {
|
default double component1() {
|
||||||
return toVec3().x();
|
return toVec3().x();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
default double component2() {
|
default double component2() {
|
||||||
return toVec3().y();
|
return toVec3().y();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
default double component3() {
|
default double component3() {
|
||||||
return toVec3().z();
|
return toVec3().z();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Contract(pure = true)
|
||||||
default double get(int i) {
|
default double get(int i) {
|
||||||
return switch (i) {
|
return switch (i) {
|
||||||
case 0 -> component1();
|
case 0 -> component1();
|
||||||
@ -27,9 +34,13 @@ public interface IVec3<T extends Record & IVec3<T>> extends IVec<T> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull Vec3 toVec3();
|
@Contract(pure = true)
|
||||||
|
default @NotNull Vec3 toVec3() {
|
||||||
|
return new Vec3(component1(), component2(), component3());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Contract(pure = true)
|
||||||
default double @NotNull [] toArray() {
|
default double @NotNull [] toArray() {
|
||||||
return new double[] {component1(), component2(), component3()};
|
return new double[] {component1(), component2(), component3()};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package eu.jonahbauer.raytracing.math;
|
package eu.jonahbauer.raytracing.math;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledWavelengths;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledWavelengths;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -1,135 +0,0 @@
|
|||||||
package eu.jonahbauer.raytracing.render;
|
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpaces;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.zip.CRC32;
|
|
||||||
import java.util.zip.CheckedOutputStream;
|
|
||||||
import java.util.zip.DeflaterOutputStream;
|
|
||||||
|
|
||||||
public enum ImageFormat {
|
|
||||||
PPM {
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException {
|
|
||||||
try (var writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.US_ASCII))) {
|
|
||||||
writer.write("P3\n");
|
|
||||||
writer.write(String.valueOf(image.getWidth()));
|
|
||||||
writer.write(" ");
|
|
||||||
writer.write(String.valueOf(image.getHeight()));
|
|
||||||
writer.write("\n255\n");
|
|
||||||
|
|
||||||
for (int y = 0; y < image.getHeight(); y++) {
|
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
|
||||||
var color = image.getRGB(x, y, ColorSpaces.sRGB);
|
|
||||||
writer.write(String.valueOf(color.red()));
|
|
||||||
writer.write(" ");
|
|
||||||
writer.write(String.valueOf(color.green()));
|
|
||||||
writer.write(" ");
|
|
||||||
writer.write(String.valueOf(color.blue()));
|
|
||||||
writer.write("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PNG {
|
|
||||||
private static final byte[] MAGIC = new byte[] { (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
|
|
||||||
private static final int IHDR_LENGTH = 13;
|
|
||||||
private static final int IHDR_TYPE = 0x49484452;
|
|
||||||
private static final int IDAT_TYPE = 0x49444154;
|
|
||||||
private static final int IEND_TYPE = 0x49454E44;
|
|
||||||
private static final int IEND_CRC = 0xAE426082;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException {
|
|
||||||
try (var data = new NoCloseDataOutputStream(out); var _ = data.closeable()) {
|
|
||||||
data.write(MAGIC);
|
|
||||||
|
|
||||||
writeIHDR(image, data);
|
|
||||||
writeIDAT(image, data);
|
|
||||||
writeIEND(image, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeIHDR(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException {
|
|
||||||
data.writeInt(IHDR_LENGTH);
|
|
||||||
try (
|
|
||||||
var crc = new CheckedOutputStream(data, new CRC32());
|
|
||||||
var ihdr = new DataOutputStream(crc)
|
|
||||||
) {
|
|
||||||
ihdr.writeInt(IHDR_TYPE);
|
|
||||||
ihdr.writeInt(image.getWidth()); // image width
|
|
||||||
ihdr.writeInt(image.getHeight()); // image height
|
|
||||||
ihdr.writeByte(8); // bit depth
|
|
||||||
ihdr.writeByte(2); // color type
|
|
||||||
ihdr.writeByte(0); // compression method
|
|
||||||
ihdr.writeByte(0); // filter method
|
|
||||||
ihdr.writeByte(0); // interlace method
|
|
||||||
ihdr.flush();
|
|
||||||
data.writeInt((int) crc.getChecksum().getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeIDAT(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException {
|
|
||||||
try (
|
|
||||||
var baos = new ByteArrayOutputStream();
|
|
||||||
var crc = new CheckedOutputStream(baos, new CRC32());
|
|
||||||
var idat = new DataOutputStream(crc)
|
|
||||||
) {
|
|
||||||
idat.writeInt(IDAT_TYPE);
|
|
||||||
|
|
||||||
try (var deflate = new DataOutputStream(new DeflaterOutputStream(idat))) {
|
|
||||||
for (int y = 0; y < image.getHeight(); y++) {
|
|
||||||
deflate.writeByte(0); // filter type
|
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
|
||||||
var pixel = image.getRGB(x, y, ColorSpaces.sRGB);
|
|
||||||
deflate.writeByte(pixel.red());
|
|
||||||
deflate.writeByte(pixel.green());
|
|
||||||
deflate.writeByte(pixel.blue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytes = baos.toByteArray();
|
|
||||||
data.writeInt(bytes.length - 4); // don't include type in length
|
|
||||||
data.write(bytes);
|
|
||||||
data.writeInt((int) crc.getChecksum().getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeIEND(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException {
|
|
||||||
data.writeInt(0);
|
|
||||||
data.writeInt(IEND_TYPE);
|
|
||||||
data.writeInt(IEND_CRC);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NoCloseDataOutputStream extends DataOutputStream {
|
|
||||||
public NoCloseDataOutputStream(OutputStream out) {
|
|
||||||
super(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
public Closeable closeable() {
|
|
||||||
return super::close;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
;
|
|
||||||
|
|
||||||
public void write(@NotNull Canvas image, @NotNull Path path) throws IOException {
|
|
||||||
try (var out = Files.newOutputStream(path)) {
|
|
||||||
write(image, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException;
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ package eu.jonahbauer.raytracing.render.camera;
|
|||||||
|
|
||||||
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.spectral.SampledWavelengths;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledWavelengths;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package eu.jonahbauer.raytracing.render.canvas;
|
package eu.jonahbauer.raytracing.render.canvas;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledWavelengths;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledWavelengths;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public interface Canvas {
|
public interface Canvas {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package eu.jonahbauer.raytracing.render.canvas;
|
package eu.jonahbauer.raytracing.render.canvas;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledWavelengths;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledWavelengths;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@ -36,7 +36,7 @@ public final class LiveCanvas implements Canvas {
|
|||||||
@Override
|
@Override
|
||||||
public void add(int x, int y, int n, @NotNull SampledSpectrum spectrum, @NotNull SampledWavelengths lambda) {
|
public void add(int x, int y, int n, @NotNull SampledSpectrum spectrum, @NotNull SampledWavelengths lambda) {
|
||||||
delegate.add(x, y, n, spectrum, lambda);
|
delegate.add(x, y, n, spectrum, lambda);
|
||||||
var color = ColorRGB.gamma(delegate.getRGB(x, y, cs));
|
var color = cs.encode(delegate.getRGB(x, y, cs));
|
||||||
var rgb = color.red() << 16 | color.green() << 8 | color.blue();
|
var rgb = color.red() << 16 | color.green() << 8 | color.blue();
|
||||||
image.setRGB(x, y, rgb);
|
image.setRGB(x, y, rgb);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package eu.jonahbauer.raytracing.render.canvas;
|
package eu.jonahbauer.raytracing.render.canvas;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledWavelengths;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledWavelengths;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package eu.jonahbauer.raytracing.render.canvas;
|
package eu.jonahbauer.raytracing.render.canvas;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledWavelengths;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledWavelengths;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
import eu.jonahbauer.raytracing.render.color.ColorXYZ;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pair of chromaticity coordinates in the xyY color space
|
* A pair of chromaticity coordinates in the xyY color space
|
@ -1,13 +1,10 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.IVec3;
|
import eu.jonahbauer.raytracing.math.IVec3;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static eu.jonahbauer.raytracing.Main.DEBUG;
|
|
||||||
|
|
||||||
public record ColorRGB(double r, double g, double b) implements IVec3<ColorRGB> {
|
public record ColorRGB(double r, double g, double b) implements IVec3<ColorRGB> {
|
||||||
public static final @NotNull ColorRGB BLACK = new ColorRGB(0.0, 0.0, 0.0);
|
public static final @NotNull ColorRGB BLACK = new ColorRGB(0.0, 0.0, 0.0);
|
||||||
public static final @NotNull ColorRGB WHITE = new ColorRGB(1.0, 1.0, 1.0);
|
public static final @NotNull ColorRGB WHITE = new ColorRGB(1.0, 1.0, 1.0);
|
||||||
@ -26,17 +23,13 @@ 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 {
|
||||||
if (DEBUG && (!Double.isFinite(r) || !Double.isFinite(g) || !Double.isFinite(b))) {
|
assert Double.isFinite(r) : "r must be finite";
|
||||||
throw new IllegalArgumentException("r, g and b must be finite");
|
assert Double.isFinite(g) : "g must be finite";
|
||||||
}
|
assert Double.isFinite(b) : "b must be finite";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -65,24 +58,6 @@ public record ColorRGB(double r, double g, double b) implements IVec3<ColorRGB>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ColorRGB gamma(@NotNull ColorRGB color) {
|
|
||||||
return new ColorRGB(gamma(color.r), gamma(color.g), gamma(color.b));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull ColorRGB inverseGamma(@NotNull ColorRGB color) {
|
|
||||||
return new ColorRGB(inverseGamma(color.r), inverseGamma(color.g), inverseGamma(color.b));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double gamma(double value) {
|
|
||||||
if (value <= 0.0031308) return 12.92 * value;
|
|
||||||
return 1.055 * Math.pow(value, 1. / 2.4) - 0.055;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double inverseGamma(double value) {
|
|
||||||
if (value <= 0.04045) return value / 12.92;
|
|
||||||
return Math.pow((value + 0.055) / 1.055, 2.4d);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ColorRGB plus(@NotNull ColorRGB other) {
|
public @NotNull ColorRGB plus(@NotNull ColorRGB other) {
|
||||||
return new ColorRGB(r + other.r, g + other.g, b + other.b);
|
return new ColorRGB(r + other.r, g + other.g, b + other.b);
|
||||||
@ -103,19 +78,6 @@ public record ColorRGB(double r, double g, double b) implements IVec3<ColorRGB>
|
|||||||
return new ColorRGB(r * other.r, g * other.g, b * other.b);
|
return new ColorRGB(r * other.r, g * other.g, b * other.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Vec3
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Vec3 toVec3() {
|
|
||||||
return new Vec3(r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull ColorRGB fromVec3(@NotNull Vec3 vec) {
|
|
||||||
return new ColorRGB(vec.x(), vec.y(), vec.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Accessors
|
* Accessors
|
||||||
*/
|
*/
|
@ -1,9 +1,8 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
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.spectral.spectrum.DenselySampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.*;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -26,16 +25,18 @@ public final class ColorSpace {
|
|||||||
private final @NotNull Matrix3 XYZfromRGB;
|
private final @NotNull Matrix3 XYZfromRGB;
|
||||||
private final @NotNull Matrix3 RGBfromXYZ;
|
private final @NotNull Matrix3 RGBfromXYZ;
|
||||||
private final @NotNull SpectrumTable RGBtoSpectrumTable;
|
private final @NotNull SpectrumTable RGBtoSpectrumTable;
|
||||||
|
private final @NotNull TransferFunction transferFunction;
|
||||||
|
|
||||||
public ColorSpace(
|
public ColorSpace(
|
||||||
@NotNull Chromaticity r, @NotNull Chromaticity g, @NotNull Chromaticity b,
|
@NotNull Chromaticity r, @NotNull Chromaticity g, @NotNull Chromaticity b,
|
||||||
@NotNull Spectrum illuminant, @NotNull SpectrumTable table
|
@NotNull Spectrum illuminant, @NotNull SpectrumTable table, @NotNull TransferFunction transferFunction
|
||||||
) {
|
) {
|
||||||
this.r = Objects.requireNonNull(r, "r");
|
this.r = Objects.requireNonNull(r, "r");
|
||||||
this.g = Objects.requireNonNull(g, "g");
|
this.g = Objects.requireNonNull(g, "g");
|
||||||
this.b = Objects.requireNonNull(b, "b");
|
this.b = Objects.requireNonNull(b, "b");
|
||||||
this.illuminant = new DenselySampledSpectrum(illuminant);
|
this.illuminant = new DenselySampledSpectrum(illuminant);
|
||||||
this.RGBtoSpectrumTable = table;
|
this.RGBtoSpectrumTable = table; // no null-check
|
||||||
|
this.transferFunction = transferFunction; // no null-check
|
||||||
|
|
||||||
this.W = illuminant.toXYZ();
|
this.W = illuminant.toXYZ();
|
||||||
this.w = W.xy();
|
this.w = W.xy();
|
||||||
@ -54,6 +55,10 @@ public final class ColorSpace {
|
|||||||
this.RGBfromXYZ = XYZfromRGB.invert();
|
this.RGBfromXYZ = XYZfromRGB.invert();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conversions
|
||||||
|
*/
|
||||||
|
|
||||||
public @NotNull ColorRGB toRGB(@NotNull ColorXYZ xyz) {
|
public @NotNull ColorRGB toRGB(@NotNull ColorXYZ xyz) {
|
||||||
var out = RGBfromXYZ.times(xyz.toVec3());
|
var out = RGBfromXYZ.times(xyz.toVec3());
|
||||||
return new ColorRGB(out.x(), out.y(), out.z());
|
return new ColorRGB(out.x(), out.y(), out.z());
|
||||||
@ -61,7 +66,7 @@ public final class ColorSpace {
|
|||||||
|
|
||||||
public @NotNull ColorXYZ toXYZ(@NotNull ColorRGB rgb) {
|
public @NotNull ColorXYZ toXYZ(@NotNull ColorRGB rgb) {
|
||||||
var out = XYZfromRGB.times(rgb.toVec3());
|
var out = XYZfromRGB.times(rgb.toVec3());
|
||||||
return ColorXYZ.fromVec3(out);
|
return new ColorXYZ(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull Vec3 toCIELab(@NotNull ColorRGB rgb) {
|
public @NotNull Vec3 toCIELab(@NotNull ColorRGB rgb) {
|
||||||
@ -85,7 +90,19 @@ public final class ColorSpace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull SigmoidPolynomial toSpectrum(@NotNull ColorRGB rgb) {
|
public @NotNull ColorRGB encode(@NotNull ColorRGB rgb) {
|
||||||
|
return transferFunction.encode(rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ColorRGB decode(@NotNull ColorRGB rgb) {
|
||||||
|
return transferFunction.decode(rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Spectrum
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 +110,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;
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectra;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectra;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -13,21 +13,21 @@ public final class ColorSpaces {
|
|||||||
new Chromaticity(0.6400, 0.3300),
|
new Chromaticity(0.6400, 0.3300),
|
||||||
new Chromaticity(0.3000, 0.6000),
|
new Chromaticity(0.3000, 0.6000),
|
||||||
new Chromaticity(0.1500, 0.0600),
|
new Chromaticity(0.1500, 0.0600),
|
||||||
Spectra.D65, read("sRGB_spectrum.bin")
|
Spectra.D65, read("sRGB_spectrum.bin"), TransferFunctions.sRGB
|
||||||
);
|
);
|
||||||
// P3-D65 (display)
|
// P3-D65 (display)
|
||||||
public static final @NotNull ColorSpace DCI_P3 = new ColorSpace(
|
public static final @NotNull ColorSpace DCI_P3 = new ColorSpace(
|
||||||
new Chromaticity(0.680, 0.320),
|
new Chromaticity(0.680, 0.320),
|
||||||
new Chromaticity(0.265, 0.690),
|
new Chromaticity(0.265, 0.690),
|
||||||
new Chromaticity(0.150, 0.060),
|
new Chromaticity(0.150, 0.060),
|
||||||
Spectra.D65, read("DCI_P3_spectrum.bin")
|
Spectra.D65, read("DCI_P3_spectrum.bin"), TransferFunctions.sRGB
|
||||||
);
|
);
|
||||||
// ITU-R Rec BT.2020
|
// ITU-R Rec BT.2020
|
||||||
public static final @NotNull ColorSpace Rec2020 = new ColorSpace(
|
public static final @NotNull ColorSpace Rec2020 = new ColorSpace(
|
||||||
new Chromaticity(0.708, 0.292),
|
new Chromaticity(0.708, 0.292),
|
||||||
new Chromaticity(0.170, 0.797),
|
new Chromaticity(0.170, 0.797),
|
||||||
new Chromaticity(0.131, 0.046),
|
new Chromaticity(0.131, 0.046),
|
||||||
Spectra.D65, read("Rec2020_spectrum.bin")
|
Spectra.D65, read("Rec2020_spectrum.bin"), null
|
||||||
);
|
);
|
||||||
|
|
||||||
private static @NotNull SpectrumTable read(@NotNull String name) {
|
private static @NotNull SpectrumTable read(@NotNull String name) {
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.IVec3;
|
import eu.jonahbauer.raytracing.math.IVec3;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
@ -25,6 +25,16 @@ public record ColorXYZ(double x, double y, double z) implements IVec3<ColorXYZ>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ColorXYZ(@NotNull Vec3 vec) {
|
||||||
|
this(vec.x(), vec.y(), vec.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColorXYZ {
|
||||||
|
assert Double.isFinite(x) : "x must be finite";
|
||||||
|
assert Double.isFinite(y) : "y must be finite";
|
||||||
|
assert Double.isFinite(z) : "z must be finite";
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Math
|
* Math
|
||||||
*/
|
*/
|
||||||
@ -71,19 +81,6 @@ public record ColorXYZ(double x, double y, double z) implements IVec3<ColorXYZ>
|
|||||||
return new ColorXYZ(x * d, y * d, z * d);
|
return new ColorXYZ(x * d, y * d, z * d);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Vec3
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Vec3 toVec3() {
|
|
||||||
return new Vec3(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull ColorXYZ fromVec3(@NotNull Vec3 vec) {
|
|
||||||
return new ColorXYZ(vec.x(), vec.y(), vec.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Accessors
|
* Accessors
|
||||||
*/
|
*/
|
@ -1,6 +1,6 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function of the form {@code s(p(x))} where {@code p} is a polynomial of second degree and {@code s} is the sigmoid
|
* A function of the form {@code s(p(x))} where {@code p} is a polynomial of second degree and {@code s} is the sigmoid
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
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.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
@ -0,0 +1,8 @@
|
|||||||
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public interface TransferFunction {
|
||||||
|
@NotNull ColorRGB decode(@NotNull ColorRGB rgb);
|
||||||
|
@NotNull ColorRGB encode(@NotNull ColorRGB rgb);
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package eu.jonahbauer.raytracing.render.color;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public final class TransferFunctions {
|
||||||
|
public static final @NotNull TransferFunction sRGB = new ComponentTransferFunction() {
|
||||||
|
@Override
|
||||||
|
protected double encode(double value) {
|
||||||
|
if (value <= 0.0031308) return 12.92 * value;
|
||||||
|
return 1.055 * Math.pow(value, 1. / 2.4) - 0.055;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double decode(double value) {
|
||||||
|
if (value <= 0.04045) return value / 12.92;
|
||||||
|
return Math.pow((value + 0.055) / 1.055, 2.4d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final @NotNull TransferFunction LINEAR = new TransferFunction() {
|
||||||
|
@Override
|
||||||
|
public @NotNull ColorRGB encode(@NotNull ColorRGB rgb) {
|
||||||
|
return rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ColorRGB decode(@NotNull ColorRGB rgb) {
|
||||||
|
return rgb;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private TransferFunctions() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static class ComponentTransferFunction implements TransferFunction {
|
||||||
|
@Override
|
||||||
|
public final @NotNull ColorRGB decode(@NotNull ColorRGB rgb) {
|
||||||
|
return new ColorRGB(decode(rgb.r()), decode(rgb.g()), decode(rgb.b()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final @NotNull ColorRGB encode(@NotNull ColorRGB rgb) {
|
||||||
|
return new ColorRGB(encode(rgb.r()), encode(rgb.g()), encode(rgb.b()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract double encode(double value);
|
||||||
|
protected abstract double decode(double value);
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
@ -0,0 +1,19 @@
|
|||||||
|
package eu.jonahbauer.raytracing.render.image;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public interface ImageWriter {
|
||||||
|
default void write(@NotNull Canvas image, @NotNull Path path) throws IOException {
|
||||||
|
try (var out = Files.newOutputStream(path)) {
|
||||||
|
write(image, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(@NotNull Canvas canvas, @NotNull OutputStream out) throws IOException;
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package eu.jonahbauer.raytracing.render.image;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
||||||
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
|
import eu.jonahbauer.raytracing.render.color.ColorSpaces;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
import java.util.zip.CheckedOutputStream;
|
||||||
|
import java.util.zip.DeflaterOutputStream;
|
||||||
|
|
||||||
|
public class PNGImageWriter implements ImageWriter {
|
||||||
|
public static final @NotNull PNGImageWriter sRGB = new PNGImageWriter(ColorSpaces.sRGB);
|
||||||
|
|
||||||
|
private static final byte[] MAGIC = new byte[] { (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||||
|
private static final int IHDR_LENGTH = 13;
|
||||||
|
private static final int IHDR_TYPE = 0x49484452;
|
||||||
|
private static final int IDAT_TYPE = 0x49444154;
|
||||||
|
private static final int IEND_TYPE = 0x49454E44;
|
||||||
|
private static final int IEND_CRC = 0xAE426082;
|
||||||
|
|
||||||
|
private final @NotNull ColorSpace cs;
|
||||||
|
|
||||||
|
public PNGImageWriter(@NotNull ColorSpace cs) {
|
||||||
|
this.cs = Objects.requireNonNull(cs, "cs");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException {
|
||||||
|
try (var data = new NoCloseDataOutputStream(out); var _ = data.closeable()) {
|
||||||
|
data.write(MAGIC);
|
||||||
|
|
||||||
|
writeIHDR(image, data);
|
||||||
|
writeIDAT(image, data);
|
||||||
|
writeIEND(image, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeIHDR(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException {
|
||||||
|
data.writeInt(IHDR_LENGTH);
|
||||||
|
try (
|
||||||
|
var crc = new CheckedOutputStream(data, new CRC32());
|
||||||
|
var ihdr = new DataOutputStream(crc)
|
||||||
|
) {
|
||||||
|
ihdr.writeInt(IHDR_TYPE);
|
||||||
|
ihdr.writeInt(image.getWidth()); // image width
|
||||||
|
ihdr.writeInt(image.getHeight()); // image height
|
||||||
|
ihdr.writeByte(8); // bit depth
|
||||||
|
ihdr.writeByte(2); // color type
|
||||||
|
ihdr.writeByte(0); // compression method
|
||||||
|
ihdr.writeByte(0); // filter method
|
||||||
|
ihdr.writeByte(0); // interlace method
|
||||||
|
ihdr.flush();
|
||||||
|
data.writeInt((int) crc.getChecksum().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeIDAT(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException {
|
||||||
|
try (
|
||||||
|
var baos = new ByteArrayOutputStream();
|
||||||
|
var crc = new CheckedOutputStream(baos, new CRC32());
|
||||||
|
var idat = new DataOutputStream(crc)
|
||||||
|
) {
|
||||||
|
idat.writeInt(IDAT_TYPE);
|
||||||
|
|
||||||
|
try (var deflate = new DataOutputStream(new DeflaterOutputStream(idat))) {
|
||||||
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
deflate.writeByte(0); // filter type
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
|
var pixel = cs.encode(image.getRGB(x, y, cs));
|
||||||
|
deflate.writeByte(pixel.red());
|
||||||
|
deflate.writeByte(pixel.green());
|
||||||
|
deflate.writeByte(pixel.blue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = baos.toByteArray();
|
||||||
|
data.writeInt(bytes.length - 4); // don't include type in length
|
||||||
|
data.write(bytes);
|
||||||
|
data.writeInt((int) crc.getChecksum().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeIEND(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException {
|
||||||
|
data.writeInt(0);
|
||||||
|
data.writeInt(IEND_TYPE);
|
||||||
|
data.writeInt(IEND_CRC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NoCloseDataOutputStream extends DataOutputStream {
|
||||||
|
public NoCloseDataOutputStream(OutputStream out) {
|
||||||
|
super(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public Closeable closeable() {
|
||||||
|
return super::close;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package eu.jonahbauer.raytracing.render.image;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
||||||
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
|
import eu.jonahbauer.raytracing.render.color.ColorSpaces;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class PPMImageWriter implements ImageWriter {
|
||||||
|
public static final PPMImageWriter sRGB = new PPMImageWriter(ColorSpaces.sRGB);
|
||||||
|
|
||||||
|
private final @NotNull ColorSpace cs;
|
||||||
|
|
||||||
|
public PPMImageWriter(@NotNull ColorSpace cs) {
|
||||||
|
this.cs = Objects.requireNonNull(cs, "cs");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException {
|
||||||
|
try (var writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.US_ASCII))) {
|
||||||
|
writer.write("P3\n");
|
||||||
|
writer.write(String.valueOf(image.getWidth()));
|
||||||
|
writer.write(" ");
|
||||||
|
writer.write(String.valueOf(image.getHeight()));
|
||||||
|
writer.write("\n255\n");
|
||||||
|
|
||||||
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
|
var color = cs.encode(image.getRGB(x, y, cs));
|
||||||
|
writer.write(String.valueOf(color.red()));
|
||||||
|
writer.write(" ");
|
||||||
|
writer.write(String.valueOf(color.green()));
|
||||||
|
writer.write(" ");
|
||||||
|
writer.write(String.valueOf(color.blue()));
|
||||||
|
writer.write("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,7 @@ 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.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectra;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBAlbedoSpectrum;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.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;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -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,10 +1,7 @@
|
|||||||
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.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBIlluminantSpectrum;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.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;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -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,8 +2,8 @@ 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.spectral.spectrum.Spectra;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectra;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.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;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -2,10 +2,7 @@ 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.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBAlbedoSpectrum;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.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;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -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.spectral.colors.ColorRGB;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.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);
|
||||||
|
@ -2,9 +2,9 @@ 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.ProbabilityDensityFunction;
|
import eu.jonahbauer.raytracing.render.renderer.pdf.ProbabilityDensityFunction;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectra;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectra;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
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;
|
||||||
|
@ -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.spectral.colors.ColorRGB;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.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");
|
||||||
|
@ -4,7 +4,7 @@ import eu.jonahbauer.raytracing.math.Ray;
|
|||||||
import eu.jonahbauer.raytracing.render.material.Material;
|
import eu.jonahbauer.raytracing.render.material.Material;
|
||||||
import eu.jonahbauer.raytracing.render.renderer.pdf.TargetingProbabilityDensityFunction;
|
import eu.jonahbauer.raytracing.render.renderer.pdf.TargetingProbabilityDensityFunction;
|
||||||
import eu.jonahbauer.raytracing.render.renderer.pdf.MixtureProbabilityDensityFunction;
|
import eu.jonahbauer.raytracing.render.renderer.pdf.MixtureProbabilityDensityFunction;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.camera.Camera;
|
import eu.jonahbauer.raytracing.render.camera.Camera;
|
||||||
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
import eu.jonahbauer.raytracing.render.canvas.Canvas;
|
||||||
import eu.jonahbauer.raytracing.scene.Scene;
|
import eu.jonahbauer.raytracing.scene.Scene;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
public final class BlackbodySpectrum implements Spectrum {
|
public final class BlackbodySpectrum implements Spectrum {
|
||||||
/**
|
/**
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A constant spectrum.
|
* A constant spectrum.
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.SigmoidPolynomial;
|
import eu.jonahbauer.raytracing.render.color.SigmoidPolynomial;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public final class RGBAlbedoSpectrum implements Spectrum {
|
public final class RGBAlbedoSpectrum implements Spectrum {
|
||||||
@ -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
|
@ -1,8 +1,8 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.SigmoidPolynomial;
|
import eu.jonahbauer.raytracing.render.color.SigmoidPolynomial;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.SigmoidPolynomial;
|
import eu.jonahbauer.raytracing.render.color.SigmoidPolynomial;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public final class RGBUnboundedSpectrum implements Spectrum {
|
public final class RGBUnboundedSpectrum implements Spectrum {
|
||||||
@ -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
|
@ -1,10 +1,9 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.IVec;
|
import eu.jonahbauer.raytracing.math.IVec;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
import eu.jonahbauer.raytracing.render.color.ColorXYZ;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
@ -1,8 +1,7 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
import eu.jonahbauer.raytracing.render.color.ColorXYZ;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectra;
|
import org.jetbrains.annotations.Contract;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -46,10 +45,12 @@ public final class SampledWavelengths {
|
|||||||
this.pdf = pdf;
|
this.pdf = pdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
public double get(int index) {
|
public double get(int index) {
|
||||||
return lambdas[index];
|
return lambdas[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
public int size() {
|
public int size() {
|
||||||
return lambdas.length;
|
return lambdas.length;
|
||||||
}
|
}
|
||||||
@ -58,12 +59,13 @@ public final class SampledWavelengths {
|
|||||||
* Terminates the secondary wavelengths. This method should be called after a wavelength-dependent operation like
|
* Terminates the secondary wavelengths. This method should be called after a wavelength-dependent operation like
|
||||||
* refraction that makes it incorrect to track multiple wavelengths together.
|
* refraction that makes it incorrect to track multiple wavelengths together.
|
||||||
*/
|
*/
|
||||||
public @NotNull SampledWavelengths collapse() {
|
@Contract(mutates = "this")
|
||||||
if (pdf.length < 2 || pdf[1] == 0) return this;
|
public double collapse() {
|
||||||
var newPdf = Arrays.copyOf(pdf, pdf.length);
|
if (pdf.length >= 2 || pdf[1] != 0) {
|
||||||
Arrays.fill(newPdf, 1, newPdf.length, 0d);
|
Arrays.fill(pdf, 1, pdf.length, 0d);
|
||||||
newPdf[0] /= newPdf.length;
|
pdf[0] /= pdf.length;
|
||||||
return new SampledWavelengths(lambdas, newPdf);
|
}
|
||||||
|
return lambdas[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
import eu.jonahbauer.raytracing.render.color.ColorXYZ;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
@ -1,12 +1,10 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
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.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
import eu.jonahbauer.raytracing.render.color.ColorXYZ;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledWavelengths;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
|
||||||
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 eu.jonahbauer.raytracing.scene.SkyBox;
|
import eu.jonahbauer.raytracing.scene.SkyBox;
|
||||||
@ -74,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
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
package eu.jonahbauer.raytracing.render.spectrum;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
package eu.jonahbauer.raytracing.render.texture;
|
package eu.jonahbauer.raytracing.render.texture;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public record CheckerTexture(double scale, @NotNull Texture even, @NotNull Texture odd) implements Texture {
|
public record CheckerTexture(double scale, @NotNull Texture even, @NotNull Texture odd) implements Texture {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package eu.jonahbauer.raytracing.render.texture;
|
package eu.jonahbauer.raytracing.render.texture;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBAlbedoSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.RGBIlluminantSpectrum;
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
@ -21,22 +19,21 @@ 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) {
|
||||||
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];
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
for (int x = 0; x < width; x++) {
|
for (int x = 0; x < width; x++) {
|
||||||
var rgb = new ColorRGB(image.getRGB(x, y));
|
var rgb = cs.decode(new ColorRGB(image.getRGB(x, y)));
|
||||||
if (gamma) rgb = ColorRGB.inverseGamma(rgb);
|
spectra[y][x] = cs.toSpectrum(rgb, type);
|
||||||
spectra[y][x] = type.newSpectrum(cs, rgb);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NotNull BufferedImage read(@NotNull String path) {
|
private static @NotNull BufferedImage read(@NotNull String path) {
|
||||||
@ -55,22 +52,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package eu.jonahbauer.raytracing.render.texture;
|
package eu.jonahbauer.raytracing.render.texture;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectra;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectra;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.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;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package eu.jonahbauer.raytracing.render.texture;
|
package eu.jonahbauer.raytracing.render.texture;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import eu.jonahbauer.raytracing.scene.HitResult;
|
import eu.jonahbauer.raytracing.scene.HitResult;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ package eu.jonahbauer.raytracing.scene;
|
|||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.AABB;
|
import eu.jonahbauer.raytracing.math.AABB;
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectra;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectra;
|
||||||
import eu.jonahbauer.raytracing.scene.util.HittableBinaryTree;
|
import eu.jonahbauer.raytracing.scene.util.HittableBinaryTree;
|
||||||
import eu.jonahbauer.raytracing.scene.util.HittableCollection;
|
import eu.jonahbauer.raytracing.scene.util.HittableCollection;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package eu.jonahbauer.raytracing.scene;
|
package eu.jonahbauer.raytracing.scene;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
import eu.jonahbauer.raytracing.math.Ray;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.SampledSpectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.SampledSpectrum;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package eu.jonahbauer.raytracing.render.canvas;
|
package eu.jonahbauer.raytracing.render.canvas;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.ImageFormat;
|
import eu.jonahbauer.raytracing.render.ImageFormat;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
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.Range;
|
||||||
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.spectral.colors.ColorRGB;
|
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||||
import eu.jonahbauer.raytracing.render.material.LambertianMaterial;
|
import eu.jonahbauer.raytracing.render.material.LambertianMaterial;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user