Compare commits
6 Commits
00fbf4e4f1
...
533461204a
Author | SHA1 | Date |
---|---|---|
jbb01 | 533461204a | 5 months ago |
jbb01 | b8aae8c2e5 | 5 months ago |
jbb01 | 9eb8afcb59 | 5 months ago |
jbb01 | 75c56c0032 | 5 months ago |
jbb01 | 903ab1409b | 5 months ago |
jbb01 | 791ee606c4 | 5 months ago |
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -1,4 +1,4 @@
|
||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
||||
package eu.jonahbauer.raytracing.render.color;
|
||||
|
||||
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.Vec3;
|
||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
||||
import eu.jonahbauer.raytracing.render.spectrum.Spectrum;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
||||
package eu.jonahbauer.raytracing.render.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.
|
@ -1,4 +1,4 @@
|
||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
||||
package eu.jonahbauer.raytracing.render.spectrum;
|
||||
|
||||
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;
|
||||
|
@ -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.render.spectral.colors.ColorSpace;
|
||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorRGB;
|
||||
import eu.jonahbauer.raytracing.render.color.ColorSpace;
|
||||
import eu.jonahbauer.raytracing.render.color.ColorXYZ;
|
||||
import eu.jonahbauer.raytracing.render.color.ColorRGB;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
@ -1,4 +1,4 @@
|
||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
||||
package eu.jonahbauer.raytracing.render.spectrum;
|
||||
|
||||
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 java.io.BufferedReader;
|
@ -1,4 +1,4 @@
|
||||
package eu.jonahbauer.raytracing.render.spectral.spectrum;
|
||||
package eu.jonahbauer.raytracing.render.spectrum;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
Loading…
Reference in New Issue