align operators in Vec3, Color, ColorXYZ and SampledSpectrum
This commit is contained in:
parent
c9db3bf94b
commit
ddc861138a
@ -82,7 +82,7 @@ public class Examples {
|
|||||||
var rnd = rng.nextDouble();
|
var rnd = rng.nextDouble();
|
||||||
if (rnd < 0.8) {
|
if (rnd < 0.8) {
|
||||||
// diffuse
|
// diffuse
|
||||||
var albedo = Color.multiply(Color.random(rng), Color.random(rng));
|
var albedo = Color.random(rng).times(Color.random(rng));
|
||||||
material = new LambertianMaterial(albedo);
|
material = new LambertianMaterial(albedo);
|
||||||
} else if (rnd < 0.95) {
|
} else if (rnd < 0.95) {
|
||||||
// metal
|
// metal
|
||||||
|
21
src/main/java/eu/jonahbauer/raytracing/math/IVec.java
Normal file
21
src/main/java/eu/jonahbauer/raytracing/math/IVec.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package eu.jonahbauer.raytracing.math;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vector-like object that implements the standard mathematical operations
|
||||||
|
* @param <T> the type
|
||||||
|
*/
|
||||||
|
public interface IVec<T extends IVec<T>> {
|
||||||
|
double get(int i);
|
||||||
|
|
||||||
|
@NotNull T plus(@NotNull T other);
|
||||||
|
@NotNull T minus(@NotNull T other);
|
||||||
|
@NotNull T times(@NotNull T other);
|
||||||
|
@NotNull T times(double d);
|
||||||
|
default @NotNull T div(double d) {
|
||||||
|
return times(1 / d);
|
||||||
|
}
|
||||||
|
|
||||||
|
double @NotNull[] toArray();
|
||||||
|
}
|
36
src/main/java/eu/jonahbauer/raytracing/math/IVec3.java
Normal file
36
src/main/java/eu/jonahbauer/raytracing/math/IVec3.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package eu.jonahbauer.raytracing.math;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vector-like object with three components.
|
||||||
|
* @param <T> the type
|
||||||
|
*/
|
||||||
|
public interface IVec3<T extends Record & IVec3<T>> extends IVec<T> {
|
||||||
|
default double component1() {
|
||||||
|
return toVec3().x();
|
||||||
|
}
|
||||||
|
default double component2() {
|
||||||
|
return toVec3().y();
|
||||||
|
}
|
||||||
|
default double component3() {
|
||||||
|
return toVec3().z();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default double get(int i) {
|
||||||
|
return switch (i) {
|
||||||
|
case 0 -> component1();
|
||||||
|
case 1 -> component2();
|
||||||
|
case 2 -> component3();
|
||||||
|
default -> throw new IndexOutOfBoundsException(i);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull Vec3 toVec3();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default double @NotNull [] toArray() {
|
||||||
|
return new double[] {component1(), component2(), component3()};
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,8 @@ import java.util.random.RandomGenerator;
|
|||||||
|
|
||||||
import static eu.jonahbauer.raytracing.Main.DEBUG;
|
import static eu.jonahbauer.raytracing.Main.DEBUG;
|
||||||
|
|
||||||
public record Vec3(double x, double y, double z) {
|
public record Vec3(double x, double y, double z) implements IVec3<Vec3> {
|
||||||
public static final Vec3 ZERO = new Vec3(0, 0, 0);
|
public static final Vec3 ZERO = new Vec3(0, 0, 0);
|
||||||
public static final Vec3 MAX = new Vec3(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
|
|
||||||
public static final Vec3 MIN = new Vec3(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE);
|
|
||||||
public static final Vec3 UNIT_X = new Vec3(1, 0, 0);
|
public static final Vec3 UNIT_X = new Vec3(1, 0, 0);
|
||||||
public static final Vec3 UNIT_Y = new Vec3(0, 1, 0);
|
public static final Vec3 UNIT_Y = new Vec3(0, 1, 0);
|
||||||
public static final Vec3 UNIT_Z = new Vec3(0, 0, 1);
|
public static final Vec3 UNIT_Z = new Vec3(0, 0, 1);
|
||||||
@ -62,7 +60,7 @@ public record Vec3(double x, double y, double z) {
|
|||||||
* @return the reflected vector
|
* @return the reflected vector
|
||||||
*/
|
*/
|
||||||
public static @NotNull Vec3 reflect(@NotNull Vec3 vec, @NotNull Vec3 normal) {
|
public static @NotNull Vec3 reflect(@NotNull Vec3 vec, @NotNull Vec3 normal) {
|
||||||
var factor = - 2 * normal.times(vec);
|
var factor = - 2 * normal.dot(vec);
|
||||||
return Vec3.fma(factor, normal, vec);
|
return Vec3.fma(factor, normal, vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +73,7 @@ public record Vec3(double x, double y, double z) {
|
|||||||
*/
|
*/
|
||||||
public static @NotNull Optional<Vec3> refract(@NotNull Vec3 vec, @NotNull Vec3 normal, double ri) {
|
public static @NotNull Optional<Vec3> refract(@NotNull Vec3 vec, @NotNull Vec3 normal, double ri) {
|
||||||
vec = vec.unit();
|
vec = vec.unit();
|
||||||
var cosTheta = Math.min(- vec.times(normal), 1.0);
|
var cosTheta = Math.min(- vec.dot(normal), 1.0);
|
||||||
var sinTheta = Math.sqrt(1 - cosTheta * cosTheta);
|
var sinTheta = Math.sqrt(1 - cosTheta * cosTheta);
|
||||||
if (ri * sinTheta > 1) return Optional.empty();
|
if (ri * sinTheta > 1) return Optional.empty();
|
||||||
|
|
||||||
@ -166,6 +164,10 @@ public record Vec3(double x, double y, double z) {
|
|||||||
return a.x * b.y * c.z + a.y * b.z * c.x + a.z * b.x * c.y - c.x * b.y * a.z - c.y * b.z * a.x - c.z * b.x * a.y;
|
return a.x * b.y * c.z + a.y * b.z * c.x + a.z * b.x * c.y - c.x * b.y * a.z - c.y * b.z * a.x - c.z * b.x * a.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Math
|
||||||
|
*/
|
||||||
|
|
||||||
public @NotNull Vec3 plus(double x, double y, double z) {
|
public @NotNull Vec3 plus(double x, double y, double z) {
|
||||||
return new Vec3(this.x + x, this.y + y, this.z + z);
|
return new Vec3(this.x + x, this.y + y, this.z + z);
|
||||||
}
|
}
|
||||||
@ -179,6 +181,7 @@ public record Vec3(double x, double y, double z) {
|
|||||||
* @param other a vector
|
* @param other a vector
|
||||||
* @return the sum of this and the other vector
|
* @return the sum of this and the other vector
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public @NotNull Vec3 plus(@NotNull Vec3 other) {
|
public @NotNull Vec3 plus(@NotNull Vec3 other) {
|
||||||
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
|
return new Vec3(this.x + other.x, this.y + other.y, this.z + other.z);
|
||||||
}
|
}
|
||||||
@ -188,55 +191,29 @@ public record Vec3(double x, double y, double z) {
|
|||||||
* @param other a vector
|
* @param other a vector
|
||||||
* @return the difference of this and the other vector
|
* @return the difference of this and the other vector
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public @NotNull Vec3 minus(@NotNull Vec3 other) {
|
public @NotNull Vec3 minus(@NotNull Vec3 other) {
|
||||||
return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z);
|
return new Vec3(this.x - other.x, this.y - other.y, this.z - other.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the scalar product of this and another vector
|
|
||||||
* @param other a vector
|
|
||||||
* @return the scalar product
|
|
||||||
*/
|
|
||||||
public double times(@NotNull Vec3 other) {
|
|
||||||
return this.x * other.x + this.y * other.y + this.z * other.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multiplies this vector with a scalar
|
* Multiplies this vector with a scalar
|
||||||
* @param t a scalar
|
* @param t a scalar
|
||||||
* @return the product of this vector and the scalar
|
* @return the product of this vector and the scalar
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public @NotNull Vec3 times(double t) {
|
public @NotNull Vec3 times(double t) {
|
||||||
return new Vec3(this.x * t, this.y * t, this.z * t);
|
return new Vec3(this.x * t, this.y * t, this.z * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Negates this vector.
|
* Multiplies this vector with another vector component-wise.
|
||||||
* {@return the negated vector}
|
|
||||||
*/
|
|
||||||
public @NotNull Vec3 neg() {
|
|
||||||
return new Vec3(-x, -y, -z);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inverts each component of this vector.
|
|
||||||
* @return the inverted vector.
|
|
||||||
*/
|
|
||||||
public @NotNull Vec3 inv() {
|
|
||||||
return new Vec3(1 / x, 1 / y, 1 / z);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the cross-product of this and another vector
|
|
||||||
* @param other a vector
|
* @param other a vector
|
||||||
* @return the cross-product
|
* @return the component-wise product of this vector and the other vector
|
||||||
*/
|
*/
|
||||||
public @NotNull Vec3 cross(@NotNull Vec3 other) {
|
@Override
|
||||||
return new Vec3(
|
public @NotNull Vec3 times(@NotNull Vec3 other) {
|
||||||
Math.fma(this.y, other.z, - other.y * this.z),
|
return new Vec3(this.x * other.x, this.y * other.y, this.z * other.z);
|
||||||
Math.fma(this.z, other.x, - other.z * this.x),
|
|
||||||
Math.fma(this.x, other.y, - other.x * this.y)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -244,8 +221,34 @@ public record Vec3(double x, double y, double z) {
|
|||||||
* @param t a scalar
|
* @param t a scalar
|
||||||
* @return this vector divided by the scalar
|
* @return this vector divided by the scalar
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public @NotNull Vec3 div(double t) {
|
public @NotNull Vec3 div(double t) {
|
||||||
return new Vec3(this.x / t, this.y / t, this.z / t);
|
return times(1 / t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the scalar product of this and another vector
|
||||||
|
* @param other a vector
|
||||||
|
* @return the scalar product
|
||||||
|
*/
|
||||||
|
public double dot(@NotNull Vec3 other) {
|
||||||
|
return this.x * other.x + this.y * other.y + this.z * other.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Vec3 neg() {
|
||||||
|
return new Vec3(-x, -y, -z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Vec3 inv() {
|
||||||
|
return new Vec3(1 / x, 1 / y, 1 / z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Vec3 cross(@NotNull Vec3 other) {
|
||||||
|
return new Vec3(
|
||||||
|
Math.fma(this.y, other.z, - other.y * this.z),
|
||||||
|
Math.fma(this.z, other.x, - other.z * this.x),
|
||||||
|
Math.fma(this.x, other.y, - other.x * this.y)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -279,17 +282,28 @@ public record Vec3(double x, double y, double z) {
|
|||||||
return div(Math.sqrt(squared));
|
return div(Math.sqrt(squared));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* {@return the n-th component of this vector}
|
* Accessors
|
||||||
* @param axis the component index
|
|
||||||
*/
|
*/
|
||||||
public double get(int axis) {
|
|
||||||
return switch (axis) {
|
@Override
|
||||||
case 0 -> x;
|
public double component1() {
|
||||||
case 1 -> y;
|
return x;
|
||||||
case 2 -> z;
|
}
|
||||||
default -> throw new IndexOutOfBoundsException(axis);
|
|
||||||
};
|
@Override
|
||||||
|
public double component2() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double component3() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Vec3 toVec3() {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull Vec3 withX(double x) {
|
public @NotNull Vec3 withX(double x) {
|
||||||
|
@ -24,7 +24,7 @@ public record DielectricMaterial(double refractionIndex, @NotNull Texture textur
|
|||||||
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 = hit.isFrontFace() ? (1 / refractionIndex) : refractionIndex;
|
||||||
|
|
||||||
var cosTheta = Math.min(- ray.direction().unit().times(hit.normal()), 1.0);
|
var cosTheta = Math.min(- ray.direction().unit().dot(hit.normal()), 1.0);
|
||||||
var reflectance = reflectance(cosTheta);
|
var reflectance = reflectance(cosTheta);
|
||||||
var reflect = reflectance > random.nextDouble();
|
var reflect = reflectance > random.nextDouble();
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package eu.jonahbauer.raytracing.render.renderer;
|
package eu.jonahbauer.raytracing.render.renderer;
|
||||||
|
|
||||||
import eu.jonahbauer.raytracing.math.Range;
|
|
||||||
import eu.jonahbauer.raytracing.math.Ray;
|
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;
|
||||||
@ -133,7 +132,7 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
var optional = scene.hit(ray);
|
var optional = scene.hit(ray);
|
||||||
if (optional.isEmpty()) {
|
if (optional.isEmpty()) {
|
||||||
var background = scene.getBackgroundColor(ray);
|
var background = scene.getBackgroundColor(ray);
|
||||||
color = Color.add(color, Color.multiply(attenuation, background));
|
color = Color.fma(attenuation, background, color);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(" Hit background: " + background);
|
System.out.println(" Hit background: " + background);
|
||||||
}
|
}
|
||||||
@ -151,7 +150,7 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result = material.scatter(ray, hit, random);
|
var result = material.scatter(ray, hit, random);
|
||||||
color = Color.add(color, Color.multiply(attenuation, emitted));
|
color = Color.fma(attenuation, emitted, color);
|
||||||
|
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@ -162,7 +161,7 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
|
|
||||||
switch (result.get()) {
|
switch (result.get()) {
|
||||||
case Material.SpecularScatterResult(var a, var scattered) -> {
|
case Material.SpecularScatterResult(var a, var scattered) -> {
|
||||||
attenuation = Color.multiply(attenuation, a);
|
attenuation = attenuation.times(a);
|
||||||
ray = scattered;
|
ray = scattered;
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@ -171,7 +170,7 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
case Material.PdfScatterResult(var a, var pdf) -> {
|
case Material.PdfScatterResult(var a, var pdf) -> {
|
||||||
if (scene.getTargets() == null) {
|
if (scene.getTargets() == null) {
|
||||||
attenuation = Color.multiply(attenuation, a);
|
attenuation = attenuation.times(a);
|
||||||
ray = new Ray(hit.position(), pdf.generate(random));
|
ray = new Ray(hit.position(), pdf.generate(random));
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@ -187,7 +186,7 @@ public final class SimpleRenderer implements Renderer {
|
|||||||
|
|
||||||
var factor = idealPdf / actualPdf;
|
var factor = idealPdf / actualPdf;
|
||||||
|
|
||||||
attenuation = Color.multiply(attenuation, Color.multiply(a, factor));
|
attenuation = attenuation.times(a.times(factor));
|
||||||
ray = new Ray(hit.position(), direction);
|
ray = new Ray(hit.position(), direction);
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
|
@ -15,7 +15,7 @@ public record CosineProbabilityDensityFunction(@NotNull Vec3 normal) implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double value(@NotNull Vec3 direction) {
|
public double value(@NotNull Vec3 direction) {
|
||||||
var cos = normal.times(direction);
|
var cos = normal.dot(direction);
|
||||||
return Math.max(0, cos / Math.PI);
|
return Math.max(0, cos / Math.PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral;
|
package eu.jonahbauer.raytracing.render.spectral;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.IVec;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
import eu.jonahbauer.raytracing.render.spectral.colors.ColorSpace;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
import eu.jonahbauer.raytracing.render.spectral.colors.ColorXYZ;
|
||||||
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
import eu.jonahbauer.raytracing.render.spectral.spectrum.Spectrum;
|
||||||
import eu.jonahbauer.raytracing.render.texture.Color;
|
import eu.jonahbauer.raytracing.render.texture.Color;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public final class SampledSpectrum {
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public final class SampledSpectrum implements IVec<SampledSpectrum> {
|
||||||
|
public static final SampledSpectrum BLACK;
|
||||||
|
public static final SampledSpectrum WHITE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
BLACK = new SampledSpectrum(new double[SampledWavelengths.SAMPLES]);
|
||||||
|
var one = new double[SampledWavelengths.SAMPLES];
|
||||||
|
Arrays.fill(one, 1);
|
||||||
|
WHITE = new SampledSpectrum(one);
|
||||||
|
}
|
||||||
|
|
||||||
private final double @NotNull[] values;
|
private final double @NotNull[] values;
|
||||||
|
|
||||||
public SampledSpectrum(@NotNull SampledWavelengths lambdas, @NotNull Spectrum spectrum) {
|
public SampledSpectrum(@NotNull SampledWavelengths lambdas, @NotNull Spectrum spectrum) {
|
||||||
@ -21,30 +34,74 @@ public final class SampledSpectrum {
|
|||||||
this.values = values;
|
this.values = values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull SampledSpectrum multiply(@NotNull SampledSpectrum a, @NotNull SampledSpectrum b) {
|
/*
|
||||||
|
* Math
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static @NotNull SampledSpectrum fma(@NotNull SampledSpectrum a, @NotNull SampledSpectrum b, @NotNull SampledSpectrum c) {
|
||||||
var out = new double[a.values.length];
|
var out = new double[a.values.length];
|
||||||
for (int i = 0; i < a.values.length; i++) {
|
for (int i = 0; i < a.values.length; i++) {
|
||||||
out[i] = a.values[i] * b.values[i];
|
out[i] = Math.fma(a.values[i], b.values[i], c.values[i]);
|
||||||
}
|
}
|
||||||
return new SampledSpectrum(out);
|
return new SampledSpectrum(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull SampledSpectrum multiply(@NotNull SampledSpectrum a, double b) {
|
public static @NotNull SampledSpectrum lerp(@NotNull SampledSpectrum a, @NotNull SampledSpectrum b, double t) {
|
||||||
|
if (t < 0) return a;
|
||||||
|
if (t > 1) return b;
|
||||||
var out = new double[a.values.length];
|
var out = new double[a.values.length];
|
||||||
for (int i = 0; i < a.values.length; i++) {
|
for (int i = 0; i < a.values.length; i++) {
|
||||||
out[i] = a.values[i] * b;
|
out[i] = Math.fma(t, b.values[i] - a.values[i], a.values[i]);
|
||||||
}
|
}
|
||||||
return new SampledSpectrum(out);
|
return new SampledSpectrum(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull SampledSpectrum add(@NotNull SampledSpectrum a, @NotNull SampledSpectrum b) {
|
@Override
|
||||||
var out = new double[a.values.length];
|
public @NotNull SampledSpectrum plus(@NotNull SampledSpectrum other) {
|
||||||
for (int i = 0; i < a.values.length; i++) {
|
var out = new double[other.values.length];
|
||||||
out[i] = a.values[i] + b.values[i];
|
for (int i = 0; i < other.values.length; i++) {
|
||||||
|
out[i] = values[i] + other.values[i];
|
||||||
}
|
}
|
||||||
return new SampledSpectrum(out);
|
return new SampledSpectrum(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SampledSpectrum minus(@NotNull SampledSpectrum other) {
|
||||||
|
var out = new double[other.values.length];
|
||||||
|
for (int i = 0; i < other.values.length; i++) {
|
||||||
|
out[i] = values[i] - other.values[i];
|
||||||
|
}
|
||||||
|
return new SampledSpectrum(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SampledSpectrum times(@NotNull SampledSpectrum other) {
|
||||||
|
var out = new double[other.values.length];
|
||||||
|
for (int i = 0; i < other.values.length; i++) {
|
||||||
|
out[i] = values[i] * other.values[i];
|
||||||
|
}
|
||||||
|
return new SampledSpectrum(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SampledSpectrum times(double d) {
|
||||||
|
var out = new double[values.length];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
out[i] = values[i] * d;
|
||||||
|
}
|
||||||
|
return new SampledSpectrum(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double @NotNull [] toArray() {
|
||||||
|
return Arrays.copyOf(values, values.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accessors
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
public double get(int index) {
|
public double get(int index) {
|
||||||
return values[index];
|
return values[index];
|
||||||
}
|
}
|
||||||
@ -53,14 +110,29 @@ public final class SampledSpectrum {
|
|||||||
return values.length;
|
return values.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double average() {
|
/*
|
||||||
double avg = 0;
|
* Object
|
||||||
for (int i = 0; i < values.length; i++) {
|
*/
|
||||||
avg = Math.fma(1d / (i + 1), values[i] - avg, avg);
|
|
||||||
}
|
@Override
|
||||||
return avg;
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof SampledSpectrum o && Arrays.equals(values, o.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String toString() {
|
||||||
|
return "SampledSpectrum[values=" + Arrays.toString(values) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conversions
|
||||||
|
*/
|
||||||
|
|
||||||
public @NotNull ColorXYZ toXYZ(@NotNull SampledWavelengths lambdas) {
|
public @NotNull ColorXYZ toXYZ(@NotNull SampledWavelengths lambdas) {
|
||||||
return lambdas.toXYZ(this);
|
return lambdas.toXYZ(this);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public final class SampledWavelengths {
|
public final class SampledWavelengths {
|
||||||
public static final int SAMPLES = 4;
|
public static final int SAMPLES = 4;
|
||||||
|
public static final SampledWavelengths EMPTY = new SampledWavelengths(new double[0], new double[0]);
|
||||||
|
|
||||||
private final double @NotNull[] lambdas;
|
private final double @NotNull[] lambdas;
|
||||||
private final double @NotNull[] pdf;
|
private final double @NotNull[] pdf;
|
||||||
@ -57,12 +58,37 @@ 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 void terminateSecondary() {
|
public @NotNull SampledWavelengths collapse() {
|
||||||
if (pdf.length < 2 || pdf[1] == 0) return;
|
if (pdf.length < 2 || pdf[1] == 0) return this;
|
||||||
Arrays.fill(pdf, 1, pdf.length, 0d);
|
var newPdf = Arrays.copyOf(pdf, pdf.length);
|
||||||
pdf[0] /= pdf.length;
|
Arrays.fill(newPdf, 1, newPdf.length, 0d);
|
||||||
|
newPdf[0] /= newPdf.length;
|
||||||
|
return new SampledWavelengths(lambdas, newPdf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Object
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof SampledWavelengths o && Arrays.equals(lambdas, o.lambdas) && Arrays.equals(pdf, o.pdf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 * Arrays.hashCode(lambdas) + Arrays.hashCode(pdf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String toString() {
|
||||||
|
return "SampledWavelengths[lambdas=" + Arrays.toString(lambdas) + ", pdf=" + Arrays.toString(pdf) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Conversions
|
||||||
|
*/
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
ColorXYZ toXYZ(@NotNull SampledSpectrum spectrum) {
|
ColorXYZ toXYZ(@NotNull SampledSpectrum spectrum) {
|
||||||
var x = Spectra.X.sample(this);
|
var x = Spectra.X.sample(this);
|
||||||
|
@ -62,7 +62,7 @@ public final class ColorSpace {
|
|||||||
|
|
||||||
public @NotNull ColorXYZ toXYZ(@NotNull Color rgb) {
|
public @NotNull ColorXYZ toXYZ(@NotNull Color rgb) {
|
||||||
var out = XYZfromRGB.times(rgb.toVec3());
|
var out = XYZfromRGB.times(rgb.toVec3());
|
||||||
return new ColorXYZ(out);
|
return ColorXYZ.fromVec3(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull Vec3 toCIELab(@NotNull Color rgb) {
|
public @NotNull Vec3 toCIELab(@NotNull Color rgb) {
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
package eu.jonahbauer.raytracing.render.spectral.colors;
|
package eu.jonahbauer.raytracing.render.spectral.colors;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.IVec3;
|
||||||
import eu.jonahbauer.raytracing.math.Vec3;
|
import eu.jonahbauer.raytracing.math.Vec3;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A CIE XYZ color
|
* A CIE XYZ color
|
||||||
*/
|
*/
|
||||||
public record ColorXYZ(double x, double y, double z) {
|
public record ColorXYZ(double x, double y, double z) implements IVec3<ColorXYZ> {
|
||||||
public static final double CIE_Y_INTEGRAL = 106.85689500000002;
|
public static final double CIE_Y_INTEGRAL = 106.85689500000002;
|
||||||
|
|
||||||
|
public static final @NotNull ColorXYZ BLACK = new ColorXYZ(0, 0, 0);
|
||||||
|
public static final @NotNull ColorXYZ WHITE = new ColorXYZ(0, 1, 0);
|
||||||
|
|
||||||
public ColorXYZ(@NotNull Chromaticity chromaticity) {
|
public ColorXYZ(@NotNull Chromaticity chromaticity) {
|
||||||
this(chromaticity, 1);
|
this(chromaticity, 1);
|
||||||
}
|
}
|
||||||
@ -21,10 +25,69 @@ public record ColorXYZ(double x, double y, double z) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColorXYZ(@NotNull Vec3 vec) {
|
/*
|
||||||
this(vec.x(), vec.y(), vec.z());
|
* Math
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static @NotNull ColorXYZ average(@NotNull ColorXYZ a, @NotNull ColorXYZ b, int index) {
|
||||||
|
return lerp(a, b, 1d / index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @NotNull ColorXYZ lerp(@NotNull ColorXYZ a, @NotNull ColorXYZ b, double t) {
|
||||||
|
if (t < 0) return a;
|
||||||
|
if (t > 1) return b;
|
||||||
|
return new ColorXYZ(
|
||||||
|
Math.fma(t, b.x - a.x, a.x),
|
||||||
|
Math.fma(t, b.y - a.y, a.y),
|
||||||
|
Math.fma(t, b.z - a.z, a.z)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull ColorXYZ fma(@NotNull ColorXYZ a, @NotNull ColorXYZ b, @NotNull ColorXYZ c) {
|
||||||
|
return new ColorXYZ(
|
||||||
|
Math.fma(a.x, b.x, c.x),
|
||||||
|
Math.fma(a.y, b.y, c.y),
|
||||||
|
Math.fma(a.z, b.z, c.z)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ColorXYZ plus(@NotNull ColorXYZ other) {
|
||||||
|
return new ColorXYZ(x + other.x, y + other.y, z + other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ColorXYZ minus(@NotNull ColorXYZ other) {
|
||||||
|
return new ColorXYZ(x - other.x, y - other.y, z - other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ColorXYZ times(@NotNull ColorXYZ other) {
|
||||||
|
return new ColorXYZ(x * other.x, y * other.y, z * other.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ColorXYZ times(double 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
|
||||||
|
*/
|
||||||
|
|
||||||
public double average() {
|
public double average() {
|
||||||
return (x + y + z) / 3;
|
return (x + y + z) / 3;
|
||||||
}
|
}
|
||||||
@ -34,19 +97,18 @@ public record ColorXYZ(double x, double y, double z) {
|
|||||||
return new Chromaticity(factor * x, factor * y);
|
return new Chromaticity(factor * x, factor * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull Vec3 toVec3() {
|
@Override
|
||||||
return new Vec3(x, y, z);
|
public double component1() {
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ColorXYZ multiply(@NotNull ColorXYZ a, @NotNull ColorXYZ b) {
|
@Override
|
||||||
return new ColorXYZ(a.x * b.x, a.y * b.y, a.z * b.z);
|
public double component2() {
|
||||||
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull ColorXYZ multiply(@NotNull ColorXYZ a, double b) {
|
@Override
|
||||||
return new ColorXYZ(a.x * b, a.y * b, a.z * b);
|
public double component3() {
|
||||||
}
|
return z;
|
||||||
|
|
||||||
public static @NotNull ColorXYZ add(@NotNull ColorXYZ a, @NotNull ColorXYZ b) {
|
|
||||||
return new ColorXYZ(a.x + b.x, a.y + b.y, a.z + b.z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,9 +97,9 @@ public final class SpectrumTable {
|
|||||||
? (color.r() > color.b() ? 0 : 2)
|
? (color.r() > color.b() ? 0 : 2)
|
||||||
: (color.g() > color.b() ? 1 : 2);
|
: (color.g() > color.b() ? 1 : 2);
|
||||||
|
|
||||||
var z = color.component(max);
|
var z = color.get(max);
|
||||||
var x = color.component((max + 1) % 3) * (resolution - 1) / z;
|
var x = color.get((max + 1) % 3) * (resolution - 1) / z;
|
||||||
var y = color.component((max + 2) % 3) * (resolution - 1) / z;
|
var y = color.get((max + 2) % 3) * (resolution - 1) / z;
|
||||||
|
|
||||||
// compute integer indices and offsets for coefficient interpolation
|
// compute integer indices and offsets for coefficient interpolation
|
||||||
int xi = Math.min((int) x, resolution - 2);
|
int xi = Math.min((int) x, resolution - 2);
|
||||||
|
@ -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 ? Color.multiply(rgb, scale) : Color.BLACK);
|
this.polynomial = cs.toSpectrum(scale == 0 ? rgb.div(scale) : Color.BLACK);
|
||||||
this.illuminant = cs.illuminant();
|
this.illuminant = cs.illuminant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ public final class RGBUnboundedSpectrum implements Spectrum {
|
|||||||
}
|
}
|
||||||
var max = Math.max(rgb.r(), Math.max(rgb.g(), rgb.b()));
|
var max = Math.max(rgb.r(), Math.max(rgb.g(), rgb.b()));
|
||||||
this.scale = 2 * max;
|
this.scale = 2 * max;
|
||||||
this.polynomial = cs.toSpectrum(scale == 0 ? Color.multiply(rgb, scale) : Color.BLACK);
|
this.polynomial = cs.toSpectrum(scale == 0 ? rgb.div(scale) : Color.BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,43 +1,18 @@
|
|||||||
package eu.jonahbauer.raytracing.render.texture;
|
package eu.jonahbauer.raytracing.render.texture;
|
||||||
|
|
||||||
|
import eu.jonahbauer.raytracing.math.IVec3;
|
||||||
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.scene.SkyBox;
|
import eu.jonahbauer.raytracing.scene.SkyBox;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static eu.jonahbauer.raytracing.Main.DEBUG;
|
import static eu.jonahbauer.raytracing.Main.DEBUG;
|
||||||
|
|
||||||
public record Color(double r, double g, double b) implements Texture, SkyBox {
|
public record Color(double r, double g, double b) implements Texture, SkyBox, IVec3<Color> {
|
||||||
public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0);
|
public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0);
|
||||||
public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
|
public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
|
||||||
public static final @NotNull Color RED = new Color(1.0, 0.0, 0.0);
|
|
||||||
public static final @NotNull Color GREEN = new Color(0.0, 1.0, 0.0);
|
|
||||||
public static final @NotNull Color BLUE = new Color(0.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
public static @NotNull Color lerp(@NotNull Color a, @NotNull Color b, double t) {
|
|
||||||
if (t < 0) return a;
|
|
||||||
if (t > 1) return b;
|
|
||||||
return new Color(
|
|
||||||
Math.fma(t, b.r, Math.fma(-t, a.r, a.r)),
|
|
||||||
Math.fma(t, b.g, Math.fma(-t, a.g, a.g)),
|
|
||||||
Math.fma(t, b.b, Math.fma(-t, a.b, a.b))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull Color multiply(@NotNull Color a, @NotNull Color b) {
|
|
||||||
return new Color(a.r() * b.r(), a.g() * b.g(), a.b() * b.b());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull Color multiply(@NotNull Color a, double b) {
|
|
||||||
return new Color(a.r() * b, a.g() * b, a.b() * b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull Color add(@NotNull Color a, @NotNull Color b) {
|
|
||||||
return new Color(a.r() + b.r(), a.g() + b.g(), a.b() + b.b());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull Color random(@NotNull Random random) {
|
public static @NotNull Color random(@NotNull Random random) {
|
||||||
return new Color(random.nextDouble(), random.nextDouble(), random.nextDouble());
|
return new Color(random.nextDouble(), random.nextDouble(), random.nextDouble());
|
||||||
@ -52,15 +27,6 @@ public record Color(double r, double g, double b) implements Texture, SkyBox {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull Color average(@NotNull Color current, @NotNull Color next, int index) {
|
|
||||||
var factor = 1d / index;
|
|
||||||
return new Color(
|
|
||||||
Math.fma(factor, next.r() - current.r(), current.r()),
|
|
||||||
Math.fma(factor, next.g() - current.g(), current.g()),
|
|
||||||
Math.fma(factor, next.b() - current.b(), current.b())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull Color gamma(@NotNull Color color, double gamma) {
|
public static @NotNull Color gamma(@NotNull Color color, double gamma) {
|
||||||
if (gamma == 1.0) {
|
if (gamma == 1.0) {
|
||||||
return color;
|
return color;
|
||||||
@ -87,21 +53,75 @@ public record Color(double r, double g, double b) implements Texture, SkyBox {
|
|||||||
this(red / 255f, green / 255f, blue / 255f);
|
this(red / 255f, green / 255f, blue / 255f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color(@NotNull Vec3 vec) {
|
|
||||||
this(vec.x(), vec.y(), vec.z());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color {
|
public Color {
|
||||||
if (DEBUG) {
|
if (DEBUG && (!Double.isFinite(r) || !Double.isFinite(g) || !Double.isFinite(b))) {
|
||||||
if (!Double.isFinite(r) || !Double.isFinite(g) || !Double.isFinite(b)) {
|
throw new IllegalArgumentException("r, g and b must be finite");
|
||||||
throw new IllegalArgumentException("r, g and b must be finite");
|
|
||||||
}
|
|
||||||
if (r < 0 || g < 0 || b < 0) {
|
|
||||||
throw new IllegalArgumentException("r, g and b must be non-negative");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Math
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static @NotNull Color average(@NotNull Color current, @NotNull Color next, int index) {
|
||||||
|
return lerp(current, next, 1d / index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull Color lerp(@NotNull Color a, @NotNull Color b, double t) {
|
||||||
|
if (t < 0) return a;
|
||||||
|
if (t > 1) return b;
|
||||||
|
return new Color(
|
||||||
|
Math.fma(t, b.r - a.r, a.r),
|
||||||
|
Math.fma(t, b.g - a.g, a.g),
|
||||||
|
Math.fma(t, b.b - a.b, a.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull Color fma(@NotNull Color a, @NotNull Color b, @NotNull Color c) {
|
||||||
|
return new Color(
|
||||||
|
Math.fma(a.r, b.r, c.r),
|
||||||
|
Math.fma(a.g, b.g, c.g),
|
||||||
|
Math.fma(a.b, b.b, c.b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Color plus(@NotNull Color other) {
|
||||||
|
return new Color(r + other.r, g + other.g, b + other.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Color minus(@NotNull Color other) {
|
||||||
|
return new Color(r - other.r, g - other.g, b - other.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Color times(double d) {
|
||||||
|
return new Color(r * d, g * d, b * d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Color times(@NotNull Color other) {
|
||||||
|
return new Color(r * other.r, g * other.g, b * other.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Vec3
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Vec3 toVec3() {
|
||||||
|
return new Vec3(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull Color fromVec3(@NotNull Vec3 vec) {
|
||||||
|
return new Color(vec.x(), vec.y(), vec.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accessors
|
||||||
|
*/
|
||||||
|
|
||||||
public int red() {
|
public int red() {
|
||||||
return toInt(r);
|
return toInt(r);
|
||||||
}
|
}
|
||||||
@ -114,35 +134,45 @@ public record Color(double r, double g, double b) implements Texture, SkyBox {
|
|||||||
return toInt(b);
|
return toInt(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double component(int i) {
|
private static int toInt(double value) {
|
||||||
return switch (i) {
|
return Math.clamp((int) (255.99 * value), 0, 255);
|
||||||
case 0 -> r;
|
|
||||||
case 1 -> g;
|
|
||||||
case 2 -> b;
|
|
||||||
default -> throw new IndexOutOfBoundsException(i);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double component1() {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double component2() {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double component3() {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Texture
|
||||||
|
*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Color get(double u, double v, @NotNull Vec3 p) {
|
public @NotNull Color get(double u, double v, @NotNull Vec3 p) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Color getColor(@NotNull Ray ray) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isUVRequired() {
|
public boolean isUVRequired() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull Vec3 toVec3() {
|
/*
|
||||||
return new Vec3(r, g, b);
|
* SkyBox
|
||||||
}
|
*/
|
||||||
|
|
||||||
private static int toInt(double value) {
|
@Override
|
||||||
return Math.clamp((int) (255.99 * value), 0, 255);
|
public @NotNull Color getColor(@NotNull Ray ray) {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ public interface SkyBox {
|
|||||||
return ray -> {
|
return ray -> {
|
||||||
// altitude from -pi/2 to pi/2
|
// altitude from -pi/2 to pi/2
|
||||||
var alt = Math.copySign(
|
var alt = Math.copySign(
|
||||||
Math.acos(ray.direction().withY(0).unit().times(ray.direction().unit())),
|
Math.acos(ray.direction().withY(0).unit().dot(ray.direction().unit())),
|
||||||
ray.direction().y()
|
ray.direction().y()
|
||||||
);
|
);
|
||||||
return Color.lerp(bottom, top, alt / Math.PI + 0.5);
|
return Color.lerp(bottom, top, alt / Math.PI + 0.5);
|
||||||
|
@ -31,23 +31,23 @@ public abstract class Hittable2D implements Hittable {
|
|||||||
var n = u.cross(v);
|
var n = u.cross(v);
|
||||||
if (n.squared() < 1e-8) throw new IllegalArgumentException();
|
if (n.squared() < 1e-8) throw new IllegalArgumentException();
|
||||||
this.normal = n.unit();
|
this.normal = n.unit();
|
||||||
this.d = origin.times(normal);
|
this.d = origin.dot(normal);
|
||||||
this.w = n.div(n.squared());
|
this.w = n.div(n.squared());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) {
|
public @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) {
|
||||||
var denominator = ray.direction().times(normal);
|
var denominator = ray.direction().dot(normal);
|
||||||
if (Math.abs(denominator) < 1e-8) return Optional.empty(); // parallel
|
if (Math.abs(denominator) < 1e-8) return Optional.empty(); // parallel
|
||||||
|
|
||||||
var t = (d - ray.origin().times(normal)) / denominator;
|
var t = (d - ray.origin().dot(normal)) / denominator;
|
||||||
if (!range.surrounds(t)) return Optional.empty();
|
if (!range.surrounds(t)) return Optional.empty();
|
||||||
|
|
||||||
var position = ray.at(t);
|
var position = ray.at(t);
|
||||||
var p = position.minus(origin);
|
var p = position.minus(origin);
|
||||||
|
|
||||||
var alpha = w.times(p.cross(v));
|
var alpha = w.dot(p.cross(v));
|
||||||
var beta = w.times(u.cross(p));
|
var beta = w.dot(u.cross(p));
|
||||||
if (!isInterior(alpha, beta)) return Optional.empty();
|
if (!isInterior(alpha, beta)) return Optional.empty();
|
||||||
|
|
||||||
var frontFace = denominator < 0;
|
var frontFace = denominator < 0;
|
||||||
@ -58,10 +58,10 @@ public abstract class Hittable2D implements Hittable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected double hit0(@NotNull Ray ray, @NotNull Range range) {
|
protected double hit0(@NotNull Ray ray, @NotNull Range range) {
|
||||||
var denominator = ray.direction().times(normal);
|
var denominator = ray.direction().dot(normal);
|
||||||
if (Math.abs(denominator) < 1e-8) return Double.NaN; // parallel
|
if (Math.abs(denominator) < 1e-8) return Double.NaN; // parallel
|
||||||
|
|
||||||
var t = (d - ray.origin().times(normal)) / denominator;
|
var t = (d - ray.origin().dot(normal)) / denominator;
|
||||||
if (!range.surrounds(t)) return Double.NaN;
|
if (!range.surrounds(t)) return Double.NaN;
|
||||||
|
|
||||||
var position = ray.at(t);
|
var position = ray.at(t);
|
||||||
|
@ -46,7 +46,7 @@ public final class Sphere implements Hittable, Target {
|
|||||||
|
|
||||||
var position = ray.at(t);
|
var position = ray.at(t);
|
||||||
var normal = Vec3.fma(invRadius, position, normalizedCenter);
|
var normal = Vec3.fma(invRadius, position, normalizedCenter);
|
||||||
var frontFace = normal.times(ray.direction()) < 0;
|
var frontFace = normal.dot(ray.direction()) < 0;
|
||||||
|
|
||||||
double u;
|
double u;
|
||||||
double v;
|
double v;
|
||||||
@ -70,7 +70,7 @@ public final class Sphere implements Hittable, Target {
|
|||||||
var oc = ray.origin().minus(center);
|
var oc = ray.origin().minus(center);
|
||||||
|
|
||||||
var a = ray.direction().squared();
|
var a = ray.direction().squared();
|
||||||
var h = ray.direction().times(oc);
|
var h = ray.direction().dot(oc);
|
||||||
var c = oc.squared() - radius * radius;
|
var c = oc.squared() - radius * radius;
|
||||||
|
|
||||||
var discriminant = h * h - a * c;
|
var discriminant = h * h - a * c;
|
||||||
|
@ -13,7 +13,7 @@ public final class PdfUtil {
|
|||||||
* must be unit vectors.
|
* must be unit vectors.
|
||||||
*/
|
*/
|
||||||
public static double getSolidAngle(@NotNull Vec3 a, @NotNull Vec3 b, @NotNull Vec3 c) {
|
public static double getSolidAngle(@NotNull Vec3 a, @NotNull Vec3 b, @NotNull Vec3 c) {
|
||||||
var angle = 2 * Math.atan(Math.abs(Vec3.tripleProduct(a, b, c)) / (1 + a.times(b) + b.times(c) + c.times(a)));
|
var angle = 2 * Math.atan(Math.abs(Vec3.tripleProduct(a, b, c)) / (1 + a.dot(b) + b.dot(c) + c.dot(a)));
|
||||||
return angle < 0 ? 2 * Math.PI + angle : angle;
|
return angle < 0 ? 2 * Math.PI + angle : angle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,10 @@ class Vec3Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void timesVec() {
|
void dotVec() {
|
||||||
var a = new Vec3(1, 2, 3);
|
var a = new Vec3(1, 2, 3);
|
||||||
var b = new Vec3(-1, 1, -2);
|
var b = new Vec3(-1, 1, -2);
|
||||||
assertEquals(-5, a.times(b));
|
assertEquals(-5, a.dot(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user