Compare commits

...

16 commits

35 changed files with 2598 additions and 78 deletions

View file

@ -2,13 +2,13 @@
<library name="google.guava" type="repository">
<properties maven-id="com.google.guava:guava:RELEASE" />
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/guava-31.1-jre.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/guava-32.1.2-jre.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/failureaccess-1.0.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jsr305-3.0.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/checker-qual-3.12.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/error_prone_annotations-2.11.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/j2objc-annotations-1.3.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/checker-qual-3.33.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/error_prone_annotations-2.18.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/j2objc-annotations-2.8.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="liberica-19" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_20" default="true" project-jdk-name="liberica-20" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View file

@ -0,0 +1,3 @@
<component name="DependencyValidationManager">
<scope name="Raytracing" pattern="(!lib:*..*&amp;&amp;!test:*..*&amp;&amp;!src:*..*&amp;&amp;!file[GfxLab]:*/&amp;&amp;!ext:*/||src:xyz.marsavic.gfxlab.graphics3d..*||src:xyz.marsavic.gfxlab.playground..*)&amp;&amp;!src:xyz.marsavic.gfxlab.playground.colorfunctions..*" />
</component>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -9,6 +9,7 @@
- Ako koristite IntelliJ, ovo je lako namestiti: File > Project Structure... > Project > SDK > Add SDK > Download JDK... > Vendor: BellSoft Liberica JDK 19.0.1.
- Alternativno, sami preuzmite JDK sa [https://bell-sw.com/pages/downloads/](https://bell-sw.com/pages/downloads/#/java-19-current). Izaberite vaš OS, poslednju verziju, i Full JDK (jedino Full JDK uključuje JavaFX). Kada instalirate/raspakujete JDK, namestite u IDE-u da projekat koristi baš taj JDK.
- Ako nećete da koristite BellSoft Liberica JDK, snađite se da preuzmete odgovarajuće biblioteke na neki način (direktni download svih potrebnih jar-fajlova, Maven, ...). Potrebni su vam javafx-base, javafx-controls, javafx-graphics, i javafx-swing.
- U nekim slučajevima JavaFX neće koristiti GPU za iscrtavanje interfejsa i sve će biti pomalo laggy (meni se to dešava uz Linux i integrisani GPU). U tom slučaju (a ni inače verovatno ne može da škodi), dodajte system property `prism.forceGPU = true`, npr. kroz VM argument `-Dprism.forceGPU=true`.
## Šta-gde

View file

@ -56,7 +56,7 @@ public class Color {
default -> null;
};
}
public static Color hsb(Vec3 v) {
return hsb(v.x(), v.y(), v.z());
@ -75,7 +75,7 @@ public class Color {
double cr = 4.0767245293f * cl - 3.3072168827f * cm + 0.2307590544f * cs;
double cg = -1.2681437731f * cl + 2.6093323231f * cm - 0.3411344290f * cs;
double cb = -0.0041119885f * cl - 0.7034763098f * cm + 1.7068625689f * cs;
return
(
cr < 0 || cr > 1 ||
@ -103,7 +103,24 @@ public class Color {
public static Color oklabPolar(Vec3 v) {
return oklabPolar(v.x(), v.y(), v.z());
}
static double clipDoubleXyz(double x) {
if (x < 0.0)
return 0.0;
return x;
}
public static Color xyz(double x, double y, double z) {
double r = 3.2404542 * x - 1.5371385 * y - 0.4985314 * z;
double g = -0.9692660 * x + 1.8760108 * y + 0.0415560 * z;
double b = 0.0556434 * x - 0.2040259 * y + 1.0572252 * z;
r = clipDoubleXyz(r);
g = clipDoubleXyz(g);
b = clipDoubleXyz(b);
return Color.rgb(r,g,b);
}
public static Color code(int code) {
return rgb(

View file

@ -0,0 +1,14 @@
package xyz.marsavic.gfxlab;
import javafx.util.Pair;
import java.util.Arrays;
import java.util.Comparator;
@FunctionalInterface
public interface Spectrum {
public double at(double wavelength);
public final Spectrum WHITE = w -> 1.0;
public final Spectrum BLACK = w -> 0.0;
}

View file

@ -0,0 +1,85 @@
package xyz.marsavic.gfxlab;
import javafx.util.Pair;
import java.util.Arrays;
import java.util.Comparator;
public class SplineSpectrum implements Spectrum {
Pair<Double, Double>[] samples;
double[] m;
public SplineSpectrum(Pair<Double, Double>[] samples) {
if (samples == null) {
throw new NullPointerException();
}
this.samples = samples;
int n = samples.length;
double[] d = new double[n - 1];
m = new double[n];
for (int i = 1; i < samples.length; i++) {
double h = samples[i].getKey() - samples[i - 1].getKey();
if (h <= 0.0)
throw new IllegalArgumentException("Samples must have strictly increasing x coordinates.");
d[i - 1] = (samples[i].getValue() - samples[i - 1].getValue());
}
m[0] = d[0];
for (int i = 1; i < n - 1; i++) {
if (d[i] == 0.0) {
m[i] = 0.0;
m[i + 1] = 0.0;
} else {
double a = m[i] / d[i];
double b = m[i + 1] / d[i];
double h = a*a+b*b;
if (h > 9.0) {
double t = 3.0 / h;
m[i] = t * a * d[i];
m[i + 1] = t * b * d[i];
}
}
}
}
@Override
public double at(double wavelength) {
final int n = samples.length;
if (wavelength <= samples[0].getKey()) {
return samples[0].getValue();
}
if (wavelength >= samples[n-1].getKey()) {
return samples[n-1].getValue();
}
int i = Arrays.binarySearch(samples,
new Pair<Double, Double>(Double.valueOf(wavelength), null),
new DoublePairKeyComparator());
if (i >= 0) {
return samples[i].getValue();
}
i = -(i+2); // Invert negative result and get index of previous element
double h = samples[i+1].getKey() - samples[i].getKey();
double t = (wavelength - samples[i].getKey()) / h;
return (samples[i].getValue() * (1 + 2*t) + h*m[i]*t) * (1 - t) * (1 - t)
+ (samples[i+1].getValue() * (3 - 2*t) + h * m[i+1] * (t - 1)) * t * t;
}
}
class DoublePairKeyComparator implements Comparator<Pair<Double, Double>> {
@Override
public int compare(Pair<Double, Double> o1, Pair<Double, Double> o2) {
double k1 = o1.getKey(), k2 = o2.getKey();
if (k1 > k2)
return 1;
else if (k1 < k2) {
return -1;
} else {
return 0;
}
}
}

View file

@ -0,0 +1,8 @@
package xyz.marsavic.gfxlab.graphics3d;
import xyz.marsavic.geometry.Vector;
public interface Camera {
Ray exitingRay(Vector sensorPosition);
}

View file

@ -2,8 +2,6 @@ package xyz.marsavic.gfxlab.graphics3d;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.random.sampling.Sampler;
import xyz.marsavic.utils.Numeric;
public class GeometryUtils {
@ -26,4 +24,12 @@ public class GeometryUtils {
}
*/
public static Vec3 reflected(Vec3 n, Vec3 d) {
return n.mul(2 * d.dot(n) / n.lengthSquared()).sub(d);
}
public static Vec3 reflectedN(Vec3 n_, Vec3 d) {
return n_.mul(2 * d.dot(n_)).sub(d);
}
}

View file

@ -7,12 +7,18 @@ import xyz.marsavic.gfxlab.Vec3;
/** Interaction of a ray with a solid.*/
public interface Hit {
/** The time of the hit */
/** The time of the hit. */
double t();
/** The normal at the point of the hit */
/** The normal at the hit point. */
Vec3 n();
/** Surface material at the hit point. */
Material material();
/** 2D coordinates in the internal coordinate system of the surface. */
Vector uv();
/** The normalized normal at the point of the hit */
default Vec3 n_() {
return n().normalized_();

View file

@ -0,0 +1,20 @@
package xyz.marsavic.gfxlab.graphics3d;
import xyz.marsavic.gfxlab.Spectrum;
import xyz.marsavic.gfxlab.Vec3;
/** Point light. */
public record Light(
Vec3 p,
Spectrum s
) {
public static Light ps(Vec3 p, Spectrum s) {
return new Light(p, s);
}
public static Light p(Vec3 p) {
return ps(p, wavelength -> 1.0);
}
}

View file

@ -0,0 +1,46 @@
package xyz.marsavic.gfxlab.graphics3d;
import javafx.util.Pair;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Spectrum;
import xyz.marsavic.gfxlab.SplineSpectrum;
public record Material(
Spectrum diffuse,
Spectrum specular,
Spectrum emissive,
double shininess,
Spectrum reflective,
Spectrum refractive,
Spectrum refractiveIndex
) {
public Material diffuse (Spectrum diffuse ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
public Material specular (Spectrum specular ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
public Material emissive (Spectrum emissive ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
public Material shininess (double shininess ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
public Material reflective (Spectrum reflective ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
public Material refractive (Spectrum refractive ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
public Material refractiveIndex(Spectrum refractiveIndex) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
// Since refractive index is a function from wavelength to a real number, it can be viewed as a spectrum
public static final Material BLACK = new Material(w -> 0, w -> 0, w -> 0, 32, w -> 0, w -> 0, w -> 1.5);
public static Material matte (Spectrum s) { return BLACK.diffuse(s); }
public static Material matte (double k) { return matte(w -> k); }
public static Material matte ( ) { return matte(w -> 1.0); } // TODO: potentially have to replace with D65
public static final Material MATTE = matte();
public static final Material MIRROR = BLACK.reflective(new SplineSpectrum(new Pair[]{
new Pair<Double, Double>(248.0, 0.926),
new Pair<Double, Double>(400.0, 0.920),
new Pair<Double, Double>(532.0, 0.916),
new Pair<Double, Double>(633.0, 0.907),
new Pair<Double, Double>(800.0, 0.868)
}));
public static final Material GLASS = BLACK.refractive(w -> 1.0)
.refractiveIndex(w -> 1.6 + (w-400)/(800-400) * (1.55 - 1.6)); /* Made to roughly resemble refractive index
of BaK4 crown glass*/
public static Material light (Spectrum s) { return BLACK.emissive(s); }
}

View file

@ -0,0 +1,39 @@
package xyz.marsavic.gfxlab.graphics3d;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Spectrum;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public interface Scene {
Solid solid();
Collection<Light> lights();
Spectrum backgroundSpectrum = wavelength -> 0;
class Base implements Scene {
protected Solid solid;
protected final List<Light> lights = new ArrayList<>();
@Override
public Solid solid() {
return solid;
}
@Override
public Collection<Light> lights() {
return lights;
}
public final Spectrum backgroundSpectrum = wavelength -> 0;
}
}

View file

@ -1,8 +1,5 @@
package xyz.marsavic.gfxlab.graphics3d;
import java.util.ArrayList;
import java.util.List;
public interface Solid {
@ -19,4 +16,12 @@ public interface Solid {
return firstHit(ray, 0);
}
default boolean hitBetween(Ray ray, double afterTime, double beforeTime) {
Hit hit = firstHit(ray);
if (hit == null) return false;
double t = hit.t();
return (afterTime < t) && (t < beforeTime);
}
}

View file

@ -0,0 +1,14 @@
package xyz.marsavic.gfxlab.graphics3d.cameras;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Camera;
import xyz.marsavic.gfxlab.graphics3d.Ray;
public class Orthographic implements Camera {
@Override
public Ray exitingRay(Vector p) {
return Ray.pd(Vec3.zp(0, p), Vec3.xyz(0, 0, 1));
}
}

View file

@ -0,0 +1,23 @@
package xyz.marsavic.gfxlab.graphics3d.cameras;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Camera;
import xyz.marsavic.gfxlab.graphics3d.Ray;
import xyz.marsavic.utils.Numeric;
public record Perspective(
double k
) implements Camera {
public static Perspective fov(double angle) {
return new Perspective(Numeric.tanT(angle / 2));
}
@Override
public Ray exitingRay(Vector p) {
return Ray.pd(Vec3.ZERO, Vec3.zp(1/k, p));
}
}

View file

@ -0,0 +1,20 @@
package xyz.marsavic.gfxlab.graphics3d.cameras;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Transformation;
import xyz.marsavic.gfxlab.graphics3d.Camera;
import xyz.marsavic.gfxlab.graphics3d.Ray;
public record TransformedCamera (
Camera source,
Transformation transformation
) implements Camera {
@Override
public Ray exitingRay(Vector sensorPosition) {
Ray ray = source.exitingRay(sensorPosition);
return transformation.at(ray);
}
}

View file

@ -0,0 +1,31 @@
package xyz.marsavic.gfxlab.graphics3d.raytracers;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.ColorFunctionT;
import xyz.marsavic.gfxlab.graphics3d.Camera;
import xyz.marsavic.gfxlab.graphics3d.Ray;
import xyz.marsavic.gfxlab.graphics3d.Scene;
public abstract class RayTracer implements ColorFunctionT {
protected final Scene scene;
protected final Camera camera;
public RayTracer(Scene scene, Camera camera) {
this.scene = scene;
this.camera = camera;
}
protected abstract Color sample(Ray r);
@Override
public Color at(double t, Vector p) {
Ray ray = camera.exitingRay(p);
return sample(ray);
}
}

View file

@ -0,0 +1,123 @@
package xyz.marsavic.gfxlab.graphics3d.raytracers;
import xyz.marsavic.geometry.Vec;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.*;
import xyz.marsavic.random.RNG;
import xyz.marsavic.utils.Numeric;
public class RayTracerSimple extends RayTracer {
private static final int spectrumSamples = 20;
private static final double minWavelength = 380;
private static final double maxWavelength = 780;
private static final double EPSILON = 1e-9;
private static final int sampleNumber = 4;
private static final double stopProbability = 1.0;
public RayTracerSimple(Scene scene, Camera camera) {
super(scene, camera);
}
public static RNG rng = new RNG();
@Override
protected Color sample(Ray ray) {
Color result = Color.BLACK;
for (int i = 0; i < sampleNumber; i++) {
double x = 0.0, y = 0.0, z = 0.0;
for (int j = 0; j < spectrumSamples; j++) {
double wavelength = minWavelength + (((double) j + rng.nextDouble()) / spectrumSamples) * (maxWavelength - minWavelength);
double intensity = sample(ray, wavelength, 7);
x += intensity * Xyz.x[(int) wavelength];
y += intensity * Xyz.y[(int) wavelength];
z += intensity * Xyz.z[(int) wavelength];
}
result = result.add(Color.xyz(x, y, z));
}
return result.div(sampleNumber);
}
protected double sample(Ray ray, double wavelength, int depthRemaining) {
double returnFactor = 1.0;
if (depthRemaining <= 0) {
if (rng.nextDouble(1.0) <= stopProbability) {
return 0.0;
} else {
returnFactor = 1/(1-stopProbability);
}
}
Hit hit = scene.solid().firstHit(ray, EPSILON);
if (hit == null) {
return scene.backgroundSpectrum.at(wavelength);
}
Vec3 p = ray.at(hit.t()); // The hit point
Vec3 n_ = hit.n_(); // Normalized normal to the body surface at the hit point
Vec3 i = ray.d().inverse(); // Incoming direction
double lI = i.length();
Vec3 r = GeometryUtils.reflectedN(n_, i); // Reflected ray (i reflected over n)
Vec3 r_ = r.div(lI); // Reflected ray (i reflected over n)
Material material = hit.material();
double lightDiffuse = 0.0; // The diffuse contribution of the generated path
double lightSpecular = 0.0; // The specular contribution of the generated path
double lightReflected = 0.0; // The reflective contribution of the generated path
double lightRefracted = 0.0; // The refractive contribution of the generated path
double lightEmissive = material.emissive().at(wavelength); // The contribution of the light surface itself
double result = lightEmissive;
double diffuse = material.diffuse().at(wavelength);
if (diffuse != 0.0) {
double r1 = rng.nextDouble(1.0); // Angle of projected random vector in the plain normal to n_
double r2 = rng.nextDouble(0.25); // Angle of vector compared to n_
Vec3 u_ = GeometryUtils.normal(n_).normalized_(); // One of the normalized vectors normal to n
Vec3 v_ = n_.cross(u_); // Doesn't need to be normalized because n_ and u_ are normalized and normal
Vec3 o = u_.mul(Numeric.sinT(r1)).add(v_.mul(Numeric.cosT(r1)))
.mul(Numeric.cosT(r2)).add(n_.mul(Numeric.sinT(r2))); // Outgoing sample vector
lightDiffuse = diffuse * sample(Ray.pd(p, o), wavelength, depthRemaining - 1);
}
result += lightDiffuse;
double reflective = material.reflective().at(wavelength);
if (reflective != 0.0) {
lightReflected = sample(Ray.pd(p, r), wavelength, depthRemaining - 1);
result += reflective * lightReflected;
}
double refractive = material.refractive().at(wavelength);
if (refractive != 0.0) {
Vec3 b; // refracted light vector
double rInd = 1/material.refractiveIndex().at(wavelength);
double iCosN = i.dot(n_);
if (iCosN < 0) {
rInd = 1/rInd;
}
Vec3 iProjection = n_.mul(iCosN);
Vec3 iRejection = i.sub(iProjection);
double iSinSqr = iRejection.lengthSquared()/i.lengthSquared();
double bSinSqr = iSinSqr*rInd*rInd;
if (bSinSqr > 1) {
b = r;
} else {
Vec3 bRejection = iRejection.inverse();
Vec3 bProjection = n_.mul(Math.sqrt(bRejection.lengthSquared()*(1-bSinSqr)/bSinSqr));
if (iCosN > 0) {
bProjection = bProjection.inverse();
}
b = bRejection.add(bProjection);
}
lightRefracted = sample(Ray.pd(p, b), wavelength, depthRemaining - 1);
result += refractive * lightRefracted;
}
return returnFactor * result;
}
}

View file

@ -1,30 +0,0 @@
package xyz.marsavic.gfxlab.graphics3d.raytracers;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.ColorFunctionT;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Hit;
import xyz.marsavic.gfxlab.graphics3d.Ray;
import xyz.marsavic.gfxlab.graphics3d.solids.Ball;
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
public class RayTracingTest implements ColorFunctionT {
Ball ball = Ball.cr(Vec3.xyz(0, 0, 2), 1);
HalfSpace halfSpace = HalfSpace.pn(Vec3.xyz(0, -1, 0), Vec3.xyz(0, 1, 0));
@Override
public Color at(double t, Vector p) {
Ray ray = Ray.pq(Vec3.ZERO, Vec3.zp(1, p));
Hit hit1 = ball.firstHit(ray);
Hit hit2 = halfSpace.firstHit(ray);
double tMin = Math.min(Hit.t(hit1), Hit.t(hit2));
return Color.gray(1.0 / (1.0 + tMin));
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
/*
package xyz.marsavic.gfxlab.graphics3d.scene;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Light;
import xyz.marsavic.gfxlab.graphics3d.Material;
import xyz.marsavic.gfxlab.graphics3d.Scene;
import xyz.marsavic.gfxlab.graphics3d.Solid;
import xyz.marsavic.gfxlab.graphics3d.solids.Ball;
import xyz.marsavic.gfxlab.graphics3d.solids.Group;
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
import xyz.marsavic.gfxlab.graphics3d.textures.Grid;
import xyz.marsavic.random.RNG;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class DiscoRoom extends Scene.Base {
public DiscoRoom(int nBalls, int nLights, long seed) {
RNG rngBalls = new RNG(2*seed);
var materialUVWalls = Grid.standard(Color.WHITE);
Collection<Solid> solids = new ArrayList<>();
Collections.addAll(solids,
HalfSpace.pn(Vec3.xyz(-1, 0, 0), Vec3.xyz( 1, 0, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 1, 0, 0), Vec3.xyz(-1, 0, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, -1, 0), Vec3.xyz( 0, 1, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, 1, 0), Vec3.xyz( 0, -1, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, 0, 1), Vec3.xyz( 0, 0, -1), materialUVWalls)
);
for (int i = 0; i < nBalls; i++) {
double hue = rngBalls.nextDouble();
Material material = rngBalls.nextDouble() < 0.8 ?
Material.matte(Color.hsb(hue, 0.9, 0.9)).specular(Color.WHITE).shininess(16) :
Material.MIRROR;
solids.add(Ball.cr(Vec3.random(rngBalls).ZOtoMP(), 0.2, uv -> material));
}
solid = Group.of(solids);
RNG rngLights = new RNG(2*seed + 1);
for (int i = 0; i < nLights; i++) {
lights.add(Light.pc(
Vec3.random(rngLights).ZOtoMP(),
Color.hsb(rngLights.nextDouble(), 0.75, 1))
);
}
}
}
*/

View file

@ -0,0 +1,54 @@
/*
package xyz.marsavic.gfxlab.graphics3d.scene;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Light;
import xyz.marsavic.gfxlab.graphics3d.Material;
import xyz.marsavic.gfxlab.graphics3d.Scene;
import xyz.marsavic.gfxlab.graphics3d.Solid;
import xyz.marsavic.gfxlab.graphics3d.solids.Ball;
import xyz.marsavic.gfxlab.graphics3d.solids.Group;
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
import xyz.marsavic.gfxlab.graphics3d.textures.Grid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class Mirrors extends Scene.Base {
public Mirrors(int nBalls) {
var materialUVWalls = Grid.standard(Color.WHITE);
var materialUVWallsL = Grid.standard(Color.hsb(0.00, 0.5, 1.0));
var materialUVWallsR = Grid.standard(Color.hsb(0.33, 0.5, 1.0));
Collection<Solid> solids = new ArrayList<>();
Collections.addAll(solids,
HalfSpace.pn(Vec3.xyz(-1, 0, 0), Vec3.xyz( 1, 0, 0), materialUVWallsL),
HalfSpace.pn(Vec3.xyz( 1, 0, 0), Vec3.xyz(-1, 0, 0), materialUVWallsR),
HalfSpace.pn(Vec3.xyz( 0, -1, 0), Vec3.xyz( 0, 1, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, 1, 0), Vec3.xyz( 0, -1, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, 0, 1), Vec3.xyz( 0, 0, -1), materialUVWalls)
);
Collections.addAll(lights,
Light.pc(Vec3.xyz(-0.8, 0.8, -0.8), Color.WHITE),
Light.pc(Vec3.xyz(-0.8, 0.8, 0.8), Color.WHITE),
Light.pc(Vec3.xyz( 0.8, 0.8, -0.8), Color.WHITE),
Light.pc(Vec3.xyz( 0.8, 0.8, 0.8), Color.WHITE)
);
for (int i = 0; i < nBalls; i++) {
Vector c = Vector.polar(0.5, 1.0 * i / nBalls);
Ball ball = Ball.cr(Vec3.zp(0, c), 0.4, uv -> Material.MIRROR);
solids.add(ball);
}
solid = Group.of(solids);
}
}
*/

View file

@ -0,0 +1,53 @@
/*
package xyz.marsavic.gfxlab.graphics3d.scene;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Light;
import xyz.marsavic.gfxlab.graphics3d.Material;
import xyz.marsavic.gfxlab.graphics3d.Scene;
import xyz.marsavic.gfxlab.graphics3d.Solid;
import xyz.marsavic.gfxlab.graphics3d.solids.Ball;
import xyz.marsavic.gfxlab.graphics3d.solids.Group;
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
import xyz.marsavic.gfxlab.graphics3d.textures.Grid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class RefractionTest extends Scene.Base {
public RefractionTest() {
var materialUVWalls = Grid.standard(Color.WHITE);
var materialUVWallsL = Grid.standard(Color.hsb(0.00, 0.5, 1.0));
var materialUVWallsR = Grid.standard(Color.hsb(0.33, 0.5, 1.0));
Collection<Solid> solids = new ArrayList<>();
Collections.addAll(solids,
HalfSpace.pn(Vec3.xyz(-1, 0, 0), Vec3.xyz( 1, 0, 0), materialUVWallsL),
HalfSpace.pn(Vec3.xyz( 1, 0, 0), Vec3.xyz(-1, 0, 0), materialUVWallsR),
HalfSpace.pn(Vec3.xyz( 0, -1, 0), Vec3.xyz( 0, 1, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, 1, 0), Vec3.xyz( 0, -1, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, 0, 1), Vec3.xyz( 0, 0, -1), materialUVWalls),
Ball.cr(Vec3.xyz(-0.3, 0.3, 0.0), 0.4, uv -> Material.GLASS.refractive(Color.hsb(0.7, 0.2, 1.0))),
Ball.cr(Vec3.xyz( 0.4, -0.4, 0.0), 0.4, uv -> Material.GLASS),
Ball.cr(Vec3.xyz(-0.3, -0.4, -0.6), 0.4, uv -> Material.GLASS.refractiveIndex(2.5)),
Ball.cr(Vec3.xyz( 0.4, 0.3, 0.6), 0.4, uv -> Material.GLASS.refractiveIndex(0.6))
);
Collections.addAll(lights,
Light.pc(Vec3.xyz(-0.7, 0.7, -0.7), Color.WHITE),
Light.pc(Vec3.xyz(-0.7, 0.7, 0.7), Color.WHITE),
Light.pc(Vec3.xyz( 0.7, 0.7, -0.7), Color.WHITE),
Light.pc(Vec3.xyz( 0.7, 0.7, 0.7), Color.WHITE)
);
solid = Group.of(solids);
}
}
*/

View file

@ -0,0 +1,35 @@
/* package xyz.marsavic.gfxlab.graphics3d.scene;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Light;
import xyz.marsavic.gfxlab.graphics3d.Material;
import xyz.marsavic.gfxlab.graphics3d.Scene;
import xyz.marsavic.gfxlab.graphics3d.solids.Ball;
import xyz.marsavic.gfxlab.graphics3d.solids.Group;
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
import java.util.Collections;
public class SceneTest1 extends Scene.Base{
public SceneTest1() {
solid = Group.of(
Ball.cr(Vec3.xyz(0, 0, 0), 1,
uv -> Material.matte(Color.hsb(uv.x() * 6, 0.8, uv.y()))
),
HalfSpace.pn(Vec3.xyz(0, -1, 0), Vec3.xyz(0, 1, 0),
uv -> Material.matte(Color.hsb(uv.x(), 0.8, 0.8))
)
);
Collections.addAll(lights,
Light.pc(Vec3.xyz(-1, 1, -1), Color.hsb(0.0, 1.0, 0.6)),
Light.pc(Vec3.xyz( 2, 0, 0), Color.gray(0.6)),
Light.pc(Vec3.xyz( 0, 0, -2), Color.gray(0.1))
);
}
}
*/

View file

@ -0,0 +1,75 @@
package xyz.marsavic.gfxlab.graphics3d.scene;
import xyz.marsavic.functions.interfaces.F1;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.SplineSpectrum;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.*;
import xyz.marsavic.gfxlab.graphics3d.solids.Ball;
import xyz.marsavic.gfxlab.graphics3d.solids.Group;
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
import xyz.marsavic.gfxlab.graphics3d.solids.Parallelepiped;
import xyz.marsavic.gfxlab.graphics3d.textures.Grid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class SpectrumTest extends Scene.Base {
public SpectrumTest() {
var materialUVWalls = (F1<Material, Vector>) (uv -> Material.matte(1.0));
var materialBlocks = (F1<Material, Vector>) (uv -> Material.matte(0.0));
var materialUVWallsL = Grid.standard(w -> 1.0);
var materialUVWallsB = Grid.standard(w -> 1.0);
var materialCoverBlock = Grid.standard(w -> 0.0);
var materialUVWallsR = Grid.standard(w -> 1.0);
var materialGlass = (F1<Material, Vector>) (uv -> Material.GLASS
.refractiveIndex(w -> 5.6 + (w-400)/(800-400) * (1.55 - 5.6)));
var materialMirror = (F1<Material, Vector>) (uv -> Material.MIRROR);
var materialLight = (F1<Material, Vector>) (uv -> Material.light(w -> 1.0));
Collection<Solid> solids = new ArrayList<>();
Collections.addAll(solids,
HalfSpace.pn(Vec3.xyz(-1, 0, 0), Vec3.xyz( 1, 0, 0), materialUVWallsL),
HalfSpace.pn(Vec3.xyz( 1, 0, 0), Vec3.xyz(-1, 0, 0), materialUVWallsR),
HalfSpace.pn(Vec3.xyz( 0, -1, 0), Vec3.xyz( 0, 1, 0), materialUVWalls),
// HalfSpace.pn(Vec3.xyz( 0, 1, 0), Vec3.xyz( 0, -1, 0), materialUVWalls),
HalfSpace.pn(Vec3.xyz( 0, 0, 1), Vec3.xyz( 0, 0, -1), materialUVWallsB),
HalfSpace.pn(Vec3.xyz( 0, 0, -6), Vec3.xyz( 0, 0, 1), materialUVWallsB),
Ball.cr(Vec3.xyz(0, 6, 0), 0.8, materialLight),
Parallelepiped.pabc(Vec3.xyz(-0.25, 0, 0.25),
Vec3.xyz(0.6, 0.8, 0),
Vec3.xyz(-0.2, -0.6, 0),
Vec3.xyz(0, 0, -0.5),
materialGlass),
Parallelepiped.pabc(Vec3.xyz(-1, 1, 1),
Vec3.xyz(0.75, 0, 0),
Vec3.xyz(0, 0.4, 0),
Vec3.xyz(0, 0, -6),
materialCoverBlock),
Parallelepiped.pabc(Vec3.xyz(0.25, 1, 1),
Vec3.xyz(2, 0, 0),
Vec3.xyz(0, 0.4, 0),
Vec3.xyz(0, 0, -6),
materialCoverBlock),
Parallelepiped.pabc(Vec3.xyz(-0.25, 1, 1),
Vec3.xyz(0.5, 0, 0),
Vec3.xyz(0, 0.4, 0),
Vec3.xyz(0, 0, -0.75),
materialCoverBlock),
Parallelepiped.pabc(Vec3.xyz(-0.25, 1, -0.25),
Vec3.xyz(0.5, 0, 0),
Vec3.xyz(0, 0.4, 0),
Vec3.xyz(0, 0, -6),
materialCoverBlock)
);
solid = Group.of(solids);
}
}

View file

@ -1,8 +1,10 @@
package xyz.marsavic.gfxlab.graphics3d.solids;
import xyz.marsavic.functions.interfaces.F1;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Hit;
import xyz.marsavic.gfxlab.graphics3d.Material;
import xyz.marsavic.gfxlab.graphics3d.Ray;
import xyz.marsavic.gfxlab.graphics3d.Solid;
import xyz.marsavic.utils.Numeric;
@ -12,21 +14,22 @@ public class Ball implements Solid {
private final Vec3 c;
private final double r;
private final F1<Material, Vector> mapMaterial;
// transient
private final double rSqr;
private Ball(Vec3 c, double r) {
private Ball(Vec3 c, double r, F1<Material, Vector> mapMaterial) {
this.c = c;
this.r = r;
rSqr = r * r;
this.mapMaterial = mapMaterial;
}
public static Ball cr(Vec3 c, double r) {
return new Ball(c, r);
public static Ball cr(Vec3 c, double r, F1<Material, Vector> mapMaterial) {
return new Ball(c, r, mapMaterial);
}
@ -69,6 +72,20 @@ public class Ball implements Solid {
return ray().at(t()).sub(c());
}
@Override
public Material material() {
return Ball.this.mapMaterial.at(uv());
}
@Override
public Vector uv() {
Vec3 n = n();
return Vector.xy(
Numeric.atan2T(n.z(), n.x()),
-2 * Numeric.asinT(n.y() / r) + 0.5
);
}
@Override
public Vec3 n_() {
return n().div(r);

View file

@ -0,0 +1,53 @@
package xyz.marsavic.gfxlab.graphics3d.solids;
import xyz.marsavic.gfxlab.graphics3d.Hit;
import xyz.marsavic.gfxlab.graphics3d.Ray;
import xyz.marsavic.gfxlab.graphics3d.Solid;
import java.util.Collection;
public class Group implements Solid {
private final Solid[] solids;
private Group(Solid... solids) {
this.solids = solids.clone();
}
public static Group of(Solid... solids) {
return new Group(solids);
}
public static Group of(Collection<Solid> solids) {
return new Group(solids.toArray(Solid[]::new));
}
@Override
public Hit firstHit(Ray ray, double afterTime) {
double minT = Double.POSITIVE_INFINITY;
Hit minHit = null;
for (Solid s : solids) {
Hit hit = s.firstHit(ray, afterTime);
double t = Hit.t(hit);
if (t < minT) {
minT = t;
minHit = hit;
}
}
return minHit;
}
@Override
public boolean hitBetween(Ray ray, double afterTime, double beforeTime) {
for (Solid s : solids) {
Hit hit = s.firstHit(ray, afterTime);
if ((hit != null) && (hit.t() < beforeTime)) {
return true;
}
}
return false;
}
}

View file

@ -1,10 +1,9 @@
package xyz.marsavic.gfxlab.graphics3d.solids;
import xyz.marsavic.functions.interfaces.F1;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.GeometryUtils;
import xyz.marsavic.gfxlab.graphics3d.Hit;
import xyz.marsavic.gfxlab.graphics3d.Ray;
import xyz.marsavic.gfxlab.graphics3d.Solid;
import xyz.marsavic.gfxlab.graphics3d.*;
public class HalfSpace implements Solid {
@ -12,6 +11,7 @@ public class HalfSpace implements Solid {
private final Vec3 p; // A point on the boundary plane
private final Vec3 e; // A vector parallel to the boundary plane.
private final Vec3 f; // A vector parallel to the boundary plane, not parallel to e.
private final F1<Material, Vector> mapMaterial;
// transient
private final Vec3 n; // A normal vector to the boundary plane
@ -20,10 +20,11 @@ public class HalfSpace implements Solid {
private HalfSpace(Vec3 p, Vec3 e, Vec3 f) {
private HalfSpace(Vec3 p, Vec3 e, Vec3 f, F1<Material, Vector> mapMaterial) {
this.p = p;
this.e = e;
this.f = f;
this.mapMaterial = mapMaterial;
this.n = e.cross(f);
n_ = n.normalized_();
@ -37,21 +38,21 @@ public class HalfSpace implements Solid {
}
public static HalfSpace pef(Vec3 p, Vec3 e, Vec3 f) {
return new HalfSpace(p, e, f);
public static HalfSpace pef(Vec3 p, Vec3 e, Vec3 f, F1<Material, Vector> mapMaterial) {
return new HalfSpace(p, e, f, mapMaterial);
}
public static HalfSpace pqr(Vec3 p, Vec3 q, Vec3 r) {
return pef(p, q.sub(p), r.sub(p));
public static HalfSpace pqr(Vec3 p, Vec3 q, Vec3 r, F1<Material, Vector> mapMaterial) {
return pef(p, q.sub(p), r.sub(p), mapMaterial);
}
public static HalfSpace pn(Vec3 p, Vec3 n) {
public static HalfSpace pn(Vec3 p, Vec3 n, F1<Material, Vector> mapMaterial) {
double nl = n.length();
Vec3 e = GeometryUtils.normal(n).normalizedTo(nl);
Vec3 f = n.cross(e).normalizedTo(nl);
return new HalfSpace(p, e, f);
return new HalfSpace(p, e, f, mapMaterial);
}
@ -113,11 +114,30 @@ public class HalfSpace implements Solid {
return n;
}
@Override
public Material material() {
return HalfSpace.this.mapMaterial.at(uv());
}
@Override
public Vec3 n_() {
return n_;
}
@Override
public Vector uv() {
Vec3 b = ray().at(t()).sub(p);
double b_e = b.dot(e) / eLSqr;
double b_f = b.dot(f) / fLSqr;
return Vector.xy(
(b_e - b_f * f_e) / sinSqr,
(b_f - b_e * e_f) / sinSqr
);
}
}
}

View file

@ -0,0 +1,108 @@
package xyz.marsavic.gfxlab.graphics3d.solids;
import xyz.marsavic.functions.interfaces.F1;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Vec3;
import xyz.marsavic.gfxlab.graphics3d.Hit;
import xyz.marsavic.gfxlab.graphics3d.Material;
import xyz.marsavic.gfxlab.graphics3d.Ray;
import xyz.marsavic.gfxlab.graphics3d.Solid;
public class Parallelepiped implements Solid {
private final Vec3 p; // starting vertex
private final Vec3[][][] vertices;
HalfSpace [] sides;
private final Vec3 a, b, c;
private Parallelepiped(Vec3 point, Vec3 aEdge, Vec3 bEdge, Vec3 cEdge, F1<Material, Vector> mapMaterial) {
this.p = point;
this.a = aEdge;
this.b = bEdge;
this.c = cEdge;
vertices = new Vec3[][][]
{{{p, p.add(c)}, {p.add(b), p.add(b).add(c)}},
{{p.add(a), p.add(a).add(c)}, {p.add(a).add(b), p.add(a).add(b).add(c)}}};
sides = new HalfSpace[6];
sides[0] = HalfSpace.pef(p, b, a, mapMaterial);
sides[1] = HalfSpace.pef(p.add(c), a, b, mapMaterial);
sides[2] = HalfSpace.pef(p, a, c, mapMaterial);
sides[3] = HalfSpace.pef(p.add(b), c, a, mapMaterial);
sides[4] = HalfSpace.pef(p, c, b, mapMaterial);
sides[5] = HalfSpace.pef(p.add(a), b, c, mapMaterial);
}
public static Parallelepiped pabc(Vec3 p, Vec3 a, Vec3 b, Vec3 c, F1<Material, Vector> mapMaterial) {
return new Parallelepiped(p, a, b, c, mapMaterial);
}
public Vec3 p() {
return p;
}
public Vec3 a() {
return a;
}
public Vec3 b() {
return b;
}
public Vec3 c() {
return c;
}
public Vec3[][][] vertices() {
return vertices.clone();
}
private static boolean pointOnParallelogram(Vec3 p, Vec3 e, Vec3 f) {
// we solve p as a linear combination of a*e+b*f, then check if this combination is in
// 0<=a<=1 and 0<=b<=1
double D = e.lengthSquared() * f.lengthSquared() - e.dot(f)*e.dot(f);
if (D == 0)
return false;
// System determinant
double Da = p.dot(e)*f.lengthSquared() - p.dot(f)*e.dot(f);
// a's determinant
double Db = e.lengthSquared()*p.dot(f) - e.dot(f)*p.dot(e);
double a = Da/D;
// a's determinant
double b = Db/D;
// b's determinant
return a >= 0 && a <= 1 && b >= 0 && b <= 1;
}
@Override
public Hit firstHit(Ray ray, double afterTime) {
Hit[] planeHits = new Hit[6];
for (int i = 0; i < 6; i++) {
planeHits[i] = sides[i].firstHit(ray, afterTime);
if (planeHits[i] == null) {
continue;
}
Vec3 rayPlaneIntersect = ray.at(planeHits[i].t());
if (!pointOnParallelogram(rayPlaneIntersect.sub(sides[i].p()), sides[i].e(), sides[i].f())) {
planeHits[i] = null;
}
}
double minT = Hit.t(null); // Positive infinity
int minI = 0;
for (int i = 0; i < 6; i++) {
if (Hit.t(planeHits[i]) < minT) {
minT = Hit.t(planeHits[i]);
minI = i;
}
}
return planeHits[minI];
}
}

View file

@ -0,0 +1,55 @@
package xyz.marsavic.gfxlab.graphics3d.textures;
import xyz.marsavic.functions.interfaces.F1;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Spectrum;
import xyz.marsavic.gfxlab.graphics3d.Material;
public class Grid implements F1<Material, Vector> {
private final Vector size, sizeLine;
private final Material material, materialLine;
// transient
private final Vector sizeLineHalf;
public Grid(Vector size, Vector sizeLine, Material material, Material materialLine) {
this.size = size;
this.sizeLine = sizeLine;
this.material = material;
this.materialLine = materialLine;
sizeLineHalf = sizeLine.div(2);
}
@Override
public Material at(Vector uv) {
Vector p = uv.add(sizeLineHalf).mod(size);
return (p.x() < sizeLine.x()) || (p.y() < sizeLine.y()) ? materialLine : material;
}
public static Grid standard(Spectrum spectrum) {
return new Grid(
Vector.xy(0.25, 0.25),
Vector.xy(0.01, 0.01),
Material.matte(spectrum),
Material.matte(w -> spectrum.at(w) * 0.75)
);
}
public static Grid standardUnit(Spectrum spectrum) {
return new Grid(
Vector.UNIT_DIAGONAL,
Vector.xy(1.0/64),
Material.matte(spectrum),
Material.matte(w -> spectrum.at(w) * 0.75)
);
}
}

View file

@ -15,10 +15,9 @@ import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Matrix;
import xyz.marsavic.gfxlab.MatrixData;
import xyz.marsavic.gfxlab.MatrixInts;
import xyz.marsavic.resources.ResourceManagerMap;
import xyz.marsavic.random.RNG;
import xyz.marsavic.resources.ResourceManagerMap;
import xyz.marsavic.time.Profiler;
import xyz.marsavic.utils.Utils;
import javax.imageio.ImageIO;
import java.io.File;
@ -139,13 +138,13 @@ public class UtilsGL {
static {
int p = ForkJoinPool.getCommonPoolParallelism();
try {
boolean obsRunning = false;
obsRunning |= ProcessHandle.allProcesses().anyMatch(ph -> ph.info().command().orElse("").contains("obs64")); // Windows
obsRunning |= !Utils.runCommand("top -b -n 1 | grep \" obs\"").isEmpty(); // Linux
obsRunning |= true;
if (obsRunning) {
p -= 3;
}
// boolean obsRunning = false;
// obsRunning |= ProcessHandle.allProcesses().anyMatch(ph -> ph.info().command().orElse("").contains("obs64")); // Windows
// obsRunning |= !Utils.runCommand("top -b -n 1 | grep \" obs\"").isEmpty(); // Linux
// obsRunning |= true;
// if (obsRunning) {
p = 1 + p/2;
// }
} catch (Exception e) {
e.printStackTrace();
}

View file

@ -4,11 +4,15 @@ import xyz.marsavic.functions.interfaces.A2;
import xyz.marsavic.functions.interfaces.F1;
import xyz.marsavic.gfxlab.*;
import xyz.marsavic.gfxlab.elements.Output;
import xyz.marsavic.gfxlab.graphics3d.raytracers.RayTracingTest;
import xyz.marsavic.gfxlab.graphics3d.Affine;
import xyz.marsavic.gfxlab.graphics3d.cameras.Perspective;
import xyz.marsavic.gfxlab.graphics3d.cameras.TransformedCamera;
import xyz.marsavic.gfxlab.graphics3d.raytracers.RayTracerSimple;
import xyz.marsavic.gfxlab.graphics3d.scene.SpectrumTest;
import xyz.marsavic.gfxlab.gui.UtilsGL;
import xyz.marsavic.gfxlab.tonemapping.ColorTransform;
import xyz.marsavic.gfxlab.tonemapping.ToneMapping;
import xyz.marsavic.gfxlab.tonemapping.colortransforms.Identity;
import xyz.marsavic.gfxlab.tonemapping.matrixcolor_to_colortransforms.AutoSoft;
import static xyz.marsavic.gfxlab.elements.ElementF.e;
import static xyz.marsavic.gfxlab.elements.Output.val;
@ -28,21 +32,33 @@ public class GfxLab {
e(Fs::aFillFrameColor,
e(Fs::transformedColorFunction,
// e(Blobs::new, val(5), val(0.1), val(0.2)),
e(RayTracingTest::new),
e(RayTracerSimple::new,
// e(RefractionTest::new),
// e(DiscoRoom::new, val(16), val(16), val(0x3361EB272FEA4C62L)),
e(SpectrumTest::new),
// e(Mirrors::new, val(3)),
e(TransformedCamera::new,
e(Perspective::new, val(1.0/3)),
// e(Orthographic::new),
e(Affine.IDENTITY
.then(Affine.translation(Vec3.xyz(0, 0, -4)))
)
)
),
e(TransformationsFromSize.toGeometric, eSize)
)
),
e(Fs::toneMapping,
e(ColorTransform::asColorTransformFromMatrixColor,
e(Identity::new)
)
// e(ColorTransform::asColorTransformFromMatrixColor,
// e(Multiply::new, val(0.05))
// )
e(AutoSoft::new, e(0x1p-5), e(1.0))
)
)
);
);
outRenderer = eRenderer.out();
}
}
@ -70,8 +86,9 @@ class Fs {
public static ToneMapping toneMapping(F1<ColorTransform, Matrix<Color>> f_ColorTransform_MatrixColor) {
return (input, output) -> {
ColorTransform f = f_ColorTransform_MatrixColor.at(input);
output.fill(p -> f.at(input.get(p)).code());
output.fill(p -> f.at(input.get(p)).codeClamp());
};
}
}

View file

@ -0,0 +1,71 @@
package xyz.marsavic.gfxlab.tonemapping.matrixcolor_to_colortransforms;
import xyz.marsavic.functions.interfaces.F1;
import xyz.marsavic.geometry.Vector;
import xyz.marsavic.gfxlab.Color;
import xyz.marsavic.gfxlab.Matrix;
import xyz.marsavic.gfxlab.gui.UtilsGL;
import xyz.marsavic.gfxlab.tonemapping.ColorTransform;
// TODO
public class AutoSoft implements F1<ColorTransform, Matrix<Color>> {
private final double preFactor;
private final double power;
private final double postFactor = 1.0;
private final boolean autoPostFactor = true;
public AutoSoft(double preFactor, double power) {
this.preFactor = preFactor;
this.power = power;
}
private double lFactor(double lSrc) {
double lPre = lSrc * preFactor;
double lDst = 1 - 1 / (1 + Math.pow(lPre, power));
double f = lDst / lSrc;
if (Double.isNaN(f)) {
f = 0;
}
return f;
}
@Override
public ColorTransform at(Matrix<Color> colorMatrix) {
Vector size = colorMatrix.size();
double postFactor_;
if (autoPostFactor) {
double[] maxY = new double[size.xInt()];
UtilsGL.parallelY(size, y -> {
maxY[y] = Double.NEGATIVE_INFINITY;
for (int x = 0; x < size.xInt(); x++) {
Color c = colorMatrix.get(x, y);
Color result = c.mul(lFactor(c.luminance()));
maxY[y] = Math.max(maxY[y], result.max());
}
});
// TODO Replace with fork-join task.
double max = Double.NEGATIVE_INFINITY;
for (int y = 0; y < size.yInt(); y++) {
max = Math.max(max, maxY[y]);
}
postFactor_ = 1 / max;
} else {
postFactor_ = postFactor;
}
return color -> color.mul(lFactor(color.luminance()) * postFactor_);
}
}