Compare commits
	
		
			2 commits
		
	
	
		
			master
			...
			kapri-ball
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						099f3e672c | ||
| 
							 | 
						82c1339388 | 
					 34 changed files with 115 additions and 2625 deletions
				
			
		
							
								
								
									
										8
									
								
								.idea/libraries/google_guava.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/libraries/google_guava.xml
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -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-32.1.2-jre.jar!/" />
 | 
			
		||||
      <root url="jar://$PROJECT_DIR$/lib/guava-31.1-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.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!/" />
 | 
			
		||||
      <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!/" />
 | 
			
		||||
    </CLASSES>
 | 
			
		||||
    <JAVADOC />
 | 
			
		||||
    <SOURCES />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								.idea/misc.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/misc.xml
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="ProjectRootManager" version="2" languageLevel="JDK_20" default="true" project-jdk-name="liberica-20" project-jdk-type="JavaSDK">
 | 
			
		||||
  <component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="liberica-19" project-jdk-type="JavaSDK">
 | 
			
		||||
    <output url="file://$PROJECT_DIR$/out" />
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										3
									
								
								.idea/scopes/Raytracing.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								.idea/scopes/Raytracing.xml
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -1,3 +0,0 @@
 | 
			
		|||
<component name="DependencyValidationManager">
 | 
			
		||||
  <scope name="Raytracing" pattern="(!lib:*..*&&!test:*..*&&!src:*..*&&!file[GfxLab]:*/&&!ext:*/||src:xyz.marsavic.gfxlab.graphics3d..*||src:xyz.marsavic.gfxlab.playground..*)&&!src:xyz.marsavic.gfxlab.playground.colorfunctions..*" />
 | 
			
		||||
</component>
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +9,6 @@
 | 
			
		|||
    - 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,23 +104,6 @@ public class Color {
 | 
			
		|||
		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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +0,0 @@
 | 
			
		|||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,85 +0,0 @@
 | 
			
		|||
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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +0,0 @@
 | 
			
		|||
package xyz.marsavic.gfxlab.graphics3d;
 | 
			
		||||
 | 
			
		||||
import xyz.marsavic.geometry.Vector;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public interface Camera {
 | 
			
		||||
	Ray exitingRay(Vector sensorPosition);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,6 +2,8 @@ package xyz.marsavic.gfxlab.graphics3d;
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
import xyz.marsavic.gfxlab.Vec3;
 | 
			
		||||
import xyz.marsavic.random.sampling.Sampler;
 | 
			
		||||
import xyz.marsavic.utils.Numeric;
 | 
			
		||||
 | 
			
		||||
public class GeometryUtils {
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -24,12 +26,4 @@ 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);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,18 +7,12 @@ 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 hit point. */
 | 
			
		||||
	/** The normal at the point of the hit */
 | 
			
		||||
	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_();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +0,0 @@
 | 
			
		|||
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);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
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); }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,39 +0,0 @@
 | 
			
		|||
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;
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,8 @@
 | 
			
		|||
package xyz.marsavic.gfxlab.graphics3d;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public interface Solid {
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -16,12 +19,4 @@ 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);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +0,0 @@
 | 
			
		|||
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));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,23 +0,0 @@
 | 
			
		|||
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));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,20 +0,0 @@
 | 
			
		|||
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);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,31 +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.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);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,123 +0,0 @@
 | 
			
		|||
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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
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
											
										
									
								
							| 
						 | 
				
			
			@ -1,61 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
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))
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,54 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
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);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,53 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
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);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,35 +0,0 @@
 | 
			
		|||
/* 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))
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,75 +0,0 @@
 | 
			
		|||
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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,35 +1,29 @@
 | 
			
		|||
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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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, F1<Material, Vector> mapMaterial) {
 | 
			
		||||
	private Ball(Vec3 c, double r) {
 | 
			
		||||
		this.c = c;
 | 
			
		||||
		this.r = r;
 | 
			
		||||
		rSqr = r * r;
 | 
			
		||||
		this.mapMaterial = mapMaterial;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	public static Ball cr(Vec3 c, double r, F1<Material, Vector> mapMaterial) {
 | 
			
		||||
		return new Ball(c, r, mapMaterial);
 | 
			
		||||
	public static Ball cr(Vec3 c, double r) {
 | 
			
		||||
		return new Ball(c, r);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,16 +40,23 @@ public class Ball implements Solid {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public HitBall firstHit(Ray ray, double afterTime) {
 | 
			
		||||
		Vec3 e = c().sub(ray.p());                                // Vector from the ray origin to the ball center
 | 
			
		||||
		
 | 
			
		||||
		double dSqr = ray.d().lengthSquared();
 | 
			
		||||
		double l = e.dot(ray.d()) / dSqr;
 | 
			
		||||
		double mSqr = l * l - (e.lengthSquared() - rSqr) / dSqr;
 | 
			
		||||
		
 | 
			
		||||
		if (mSqr > 0) {
 | 
			
		||||
			double m = Math.sqrt(mSqr);
 | 
			
		||||
			if (l - m > afterTime) return new HitBall(ray, l - m);
 | 
			
		||||
			if (l + m > afterTime) return new HitBall(ray, l + m);
 | 
			
		||||
		// U nasoj jednacini At^2 + Bt + C = 0, gde je t vreme kada zrak pogadja dve tacke sfere
 | 
			
		||||
		// diskriminanta ce nam dati da li trazeno t postoji i da li postoje 1 ili 2, ako postoje 2,
 | 
			
		||||
		// iz diskriminante mozemo dobiti i njihove udaljenosti.
 | 
			
		||||
		// Za sferu (x-xc)^2 + (y-yc)^2 + (z-zc)^2 = r^2 i pravu (x,y,z) = (x0,y0,z0) + t(a,b,c)
 | 
			
		||||
		// (a,b,c) je ray.d(), a x0,y0,z0 je ray.p(), dok je (xc,yc,zc) centar sfere i r poluprecnik.
 | 
			
		||||
		double A = ray.d().lengthSquared(), B = 2*ray.d().dot(ray.p().sub(c)), C = ray.p().sub(c).lengthSquared() - rSqr;
 | 
			
		||||
		//     A = a^2+b^2+c^2,             B = 2(a(x0-xc)+b(y0-yc)+c(z0-zc)), C = (x0-xc)^2+(y0-yc)^2+(z0-zc)^2-r^2
 | 
			
		||||
		double D = B*B - 4*A*C;
 | 
			
		||||
		if (D > 0) {
 | 
			
		||||
			double rootD = Math.sqrt(D);
 | 
			
		||||
			// t = (-B+-sqrt(D))/2A
 | 
			
		||||
			double tSmall = (-B-rootD)/(2*A);
 | 
			
		||||
			if (afterTime < tSmall)
 | 
			
		||||
				return new HitBall(ray, tSmall);
 | 
			
		||||
			double tBig = (-B+rootD)/(2*A);
 | 
			
		||||
			if (afterTime < tBig)
 | 
			
		||||
				return new HitBall(ray, tBig);
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -72,20 +73,6 @@ 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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,53 +0,0 @@
 | 
			
		|||
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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +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.*;
 | 
			
		||||
import xyz.marsavic.gfxlab.graphics3d.GeometryUtils;
 | 
			
		||||
import xyz.marsavic.gfxlab.graphics3d.Hit;
 | 
			
		||||
import xyz.marsavic.gfxlab.graphics3d.Ray;
 | 
			
		||||
import xyz.marsavic.gfxlab.graphics3d.Solid;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public class HalfSpace implements Solid {
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +12,6 @@ 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,11 +20,10 @@ public class HalfSpace implements Solid {
 | 
			
		|||
	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	private HalfSpace(Vec3 p, Vec3 e, Vec3 f, F1<Material, Vector> mapMaterial) {
 | 
			
		||||
	private HalfSpace(Vec3 p, Vec3 e, Vec3 f) {
 | 
			
		||||
		this.p = p;
 | 
			
		||||
		this.e = e;
 | 
			
		||||
		this.f = f;
 | 
			
		||||
		this.mapMaterial = mapMaterial;
 | 
			
		||||
		this.n = e.cross(f);
 | 
			
		||||
		
 | 
			
		||||
		n_ = n.normalized_();
 | 
			
		||||
| 
						 | 
				
			
			@ -38,21 +37,21 @@ public class HalfSpace implements Solid {
 | 
			
		|||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	public static HalfSpace pef(Vec3 p, Vec3 e, Vec3 f, F1<Material, Vector> mapMaterial) {
 | 
			
		||||
		return new HalfSpace(p, e, f, mapMaterial);
 | 
			
		||||
	public static HalfSpace pef(Vec3 p, Vec3 e, Vec3 f) {
 | 
			
		||||
		return new HalfSpace(p, e, f);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	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 pqr(Vec3 p, Vec3 q, Vec3 r) {
 | 
			
		||||
		return pef(p, q.sub(p), r.sub(p));
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	public static HalfSpace pn(Vec3 p, Vec3 n, F1<Material, Vector> mapMaterial) {
 | 
			
		||||
	public static HalfSpace pn(Vec3 p, Vec3 n) {
 | 
			
		||||
		double nl = n.length();
 | 
			
		||||
		Vec3 e = GeometryUtils.normal(n).normalizedTo(nl);
 | 
			
		||||
		Vec3 f = n.cross(e).normalizedTo(nl);
 | 
			
		||||
		return new HalfSpace(p, e, f, mapMaterial);
 | 
			
		||||
		return new HalfSpace(p, e, f);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -114,30 +113,11 @@ 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
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,108 +0,0 @@
 | 
			
		|||
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];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,55 +0,0 @@
 | 
			
		|||
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)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -15,9 +15,10 @@ import xyz.marsavic.gfxlab.Color;
 | 
			
		|||
import xyz.marsavic.gfxlab.Matrix;
 | 
			
		||||
import xyz.marsavic.gfxlab.MatrixData;
 | 
			
		||||
import xyz.marsavic.gfxlab.MatrixInts;
 | 
			
		||||
import xyz.marsavic.random.RNG;
 | 
			
		||||
import xyz.marsavic.resources.ResourceManagerMap;
 | 
			
		||||
import xyz.marsavic.random.RNG;
 | 
			
		||||
import xyz.marsavic.time.Profiler;
 | 
			
		||||
import xyz.marsavic.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import javax.imageio.ImageIO;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
| 
						 | 
				
			
			@ -138,17 +139,17 @@ 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 = 1 + p/2;
 | 
			
		||||
//			}
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
		parallelism = p;
 | 
			
		||||
		parallelism = 3;
 | 
			
		||||
		pool = new ForkJoinPool(parallelism);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,15 +4,11 @@ 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.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.graphics3d.raytracers.RayTracingTest;
 | 
			
		||||
import xyz.marsavic.gfxlab.gui.UtilsGL;
 | 
			
		||||
import xyz.marsavic.gfxlab.tonemapping.ColorTransform;
 | 
			
		||||
import xyz.marsavic.gfxlab.tonemapping.ToneMapping;
 | 
			
		||||
import xyz.marsavic.gfxlab.tonemapping.matrixcolor_to_colortransforms.AutoSoft;
 | 
			
		||||
import xyz.marsavic.gfxlab.tonemapping.colortransforms.Identity;
 | 
			
		||||
 | 
			
		||||
import static xyz.marsavic.gfxlab.elements.ElementF.e;
 | 
			
		||||
import static xyz.marsavic.gfxlab.elements.Output.val;
 | 
			
		||||
| 
						 | 
				
			
			@ -32,33 +28,21 @@ public class GfxLab {
 | 
			
		|||
								e(Fs::aFillFrameColor,
 | 
			
		||||
										e(Fs::transformedColorFunction,
 | 
			
		||||
//												e(Blobs::new, val(5), val(0.1), val(0.2)),
 | 
			
		||||
												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(RayTracingTest::new),
 | 
			
		||||
												e(TransformationsFromSize.toGeometric, eSize)
 | 
			
		||||
										)
 | 
			
		||||
								),
 | 
			
		||||
								e(Fs::toneMapping,
 | 
			
		||||
//										e(ColorTransform::asColorTransformFromMatrixColor,
 | 
			
		||||
//												e(Multiply::new, val(0.05))
 | 
			
		||||
//										)
 | 
			
		||||
										e(AutoSoft::new, e(0x1p-5), e(1.0))
 | 
			
		||||
										e(ColorTransform::asColorTransformFromMatrixColor,
 | 
			
		||||
												e(Identity::new)
 | 
			
		||||
										)
 | 
			
		||||
								)
 | 
			
		||||
						)
 | 
			
		||||
		);
 | 
			
		||||
				);
 | 
			
		||||
		
 | 
			
		||||
		outRenderer = eRenderer.out();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -86,9 +70,8 @@ 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)).codeClamp());
 | 
			
		||||
			output.fill(p -> f.at(input.get(p)).code());
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,71 +0,0 @@
 | 
			
		|||
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_);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue