Compare commits
	
		
			No commits in common. "master" and "kapri-ortho-refraction" have entirely different histories.
		
	
	
		
			master
			...
			kapri-orth
		
	
		
					 18 changed files with 107 additions and 1906 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> | ||||
|  | @ -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,20 +1,20 @@ | |||
| package xyz.marsavic.gfxlab.graphics3d; | ||||
| 
 | ||||
| import xyz.marsavic.gfxlab.Spectrum; | ||||
| import xyz.marsavic.gfxlab.Color; | ||||
| import xyz.marsavic.gfxlab.Vec3; | ||||
| 
 | ||||
| /** Point light. */ | ||||
| public record Light( | ||||
| 		Vec3 p, | ||||
| 		Spectrum s | ||||
| 		Color c | ||||
| ) { | ||||
| 	 | ||||
| 	public static Light ps(Vec3 p, Spectrum s) { | ||||
| 		return new Light(p, s); | ||||
| 	public static Light pc(Vec3 p, Color c) { | ||||
| 		return new Light(p, c); | ||||
| 	} | ||||
| 	 | ||||
| 	public static Light p(Vec3 p) { | ||||
| 		return ps(p, wavelength -> 1.0); | ||||
| 		return pc(p, Color.WHITE); | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
|  |  | |||
|  | @ -1,46 +1,30 @@ | |||
| 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, | ||||
| 		Color diffuse, | ||||
| 		Color specular, | ||||
| 		double shininess, | ||||
| 		Spectrum reflective, | ||||
| 		Spectrum refractive, | ||||
| 		Spectrum refractiveIndex | ||||
| 		Color reflective, | ||||
| 		Color refractive, | ||||
| 		double 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 Material diffuse        (Color  diffuse        ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); } | ||||
| 	public Material specular       (Color  specular       ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); } | ||||
| 	public Material shininess      (double shininess      ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); } | ||||
| 	public Material reflective     (Color  reflective     ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); } | ||||
| 	public Material refractive     (Color  refractive     ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); } | ||||
| 	public Material refractiveIndex(double refractiveIndex) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); } | ||||
| 	 | ||||
| 	public static final Material BLACK   = new Material(w -> 0, w -> 0, w -> 0, 32, w -> 0, w -> 0, w -> 1.5); | ||||
| 	public static final Material BLACK   = new Material(Color.BLACK, Color.BLACK, 32, Color.BLACK, Color.BLACK, 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 Material matte (Color  c) { return BLACK.diffuse(c); } | ||||
| 	public static Material matte (double k) { return matte(Color.gray(k)); } | ||||
| 	public static Material matte (        ) { return matte(Color.WHITE); } | ||||
| 	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); } | ||||
| 	public static final Material MIRROR = BLACK.reflective(Color.WHITE); | ||||
| 	public static final Material GLASS = BLACK.refractive(Color.WHITE).refractiveIndex(1.5); | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| package xyz.marsavic.gfxlab.graphics3d; | ||||
| 
 | ||||
| import xyz.marsavic.gfxlab.Color; | ||||
| import xyz.marsavic.gfxlab.Spectrum; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
|  | @ -13,13 +12,16 @@ public interface Scene { | |||
| 	 | ||||
| 	Collection<Light> lights(); | ||||
| 	 | ||||
| 	Spectrum backgroundSpectrum = wavelength -> 0; | ||||
| 	default Color colorBackground() { | ||||
| 		return Color.BLACK; | ||||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	class Base implements Scene { | ||||
| 		 | ||||
| 		protected Solid solid; | ||||
| 		protected final List<Light> lights = new ArrayList<>(); | ||||
| 		protected final Color colorBackground = Color.BLACK; | ||||
| 		 | ||||
| 		@Override | ||||
| 		public Solid solid() { | ||||
|  | @ -31,7 +33,10 @@ public interface Scene { | |||
| 			return lights; | ||||
| 		} | ||||
| 		 | ||||
| 		public final Spectrum backgroundSpectrum = wavelength -> 0; | ||||
| 		@Override | ||||
| 		public Color colorBackground() { | ||||
| 			return colorBackground; | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| 	 | ||||
|  |  | |||
|  | @ -1,59 +1,31 @@ | |||
| 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]; | ||||
| 		return sample(ray, 64); | ||||
| 	} | ||||
| 	 | ||||
| 			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); | ||||
| 			} | ||||
| 	protected Color sample(Ray ray, int depthRemaining) { | ||||
| 		if (depthRemaining == 0) { | ||||
| 			return Color.BLACK; | ||||
| 		} | ||||
| 		 | ||||
| 		Hit hit = scene.solid().firstHit(ray, EPSILON); | ||||
| 		if (hit == null) { | ||||
| 			return scene.backgroundSpectrum.at(wavelength); | ||||
| 			return scene.colorBackground(); | ||||
| 		} | ||||
| 		 | ||||
| 		Vec3 p = ray.at(hit.t());                 // The hit point | ||||
|  | @ -63,38 +35,50 @@ public class RayTracerSimple extends RayTracer { | |||
| 		Vec3 r = GeometryUtils.reflectedN(n_, i); // Reflected ray (i reflected over n) | ||||
| 		Vec3 r_ = r.div(lI);                      // Reflected ray (i reflected over n) | ||||
| 		 | ||||
| 		Color lightDiffuse  = Color.BLACK;        // The sum of diffuse contributions from all the lights | ||||
| 		Color lightSpecular = Color.BLACK;        // The sum of specular contributions from all the lights | ||||
| 		 | ||||
| 		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 | ||||
| 		for (Light light : scene.lights()) { | ||||
| 			Vec3 l = light.p().sub(p);            // Vector from p to the light; | ||||
| 			 | ||||
| 		double result = lightEmissive; | ||||
| 			Ray rayToLight = Ray.pd(p, l); | ||||
| 			if (scene.solid().hitBetween(rayToLight, EPSILON, 1)) continue; | ||||
| 			 | ||||
| 		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); | ||||
| 			double lLSqr = l.lengthSquared();     // Distance from p to the light squared | ||||
| 			double lL = Math.sqrt(lLSqr);         // Distance from p to the light | ||||
| 			double cosLN = n_.dot(l) / lL;        // Cosine of the angle between l and n_ | ||||
| 			 | ||||
| 			if (cosLN > 0) {                      // If the light is above the surface | ||||
| 				Color irradiance = light.c().mul(cosLN / lLSqr); | ||||
| 				// The irradiance represents how much light is received by a unit area of the surface. It is | ||||
| 				// proportional to the cosine of the incoming angle and inversely proportional to the distance squared | ||||
| 				// (inverse-square law). | ||||
| 				lightDiffuse = lightDiffuse.add(irradiance); | ||||
| 				 | ||||
| 				double cosLR = l.dot(r_); | ||||
| 				if (cosLR > 0) {                  // If the angle between l and r is acute | ||||
| 					cosLR /= lL; | ||||
| 					lightSpecular = lightSpecular.add(irradiance.mul(Math.pow(cosLR, material.shininess()))); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		result += lightDiffuse; | ||||
| 		double reflective = material.reflective().at(wavelength); | ||||
| 		if (reflective != 0.0) { | ||||
| 			lightReflected = sample(Ray.pd(p, r), wavelength, depthRemaining - 1); | ||||
| 			result += reflective * lightReflected; | ||||
| 		Color result = Color.BLACK; | ||||
| 		result = result.add(material.diffuse ().mul(lightDiffuse )); | ||||
| 		result = result.add(material.specular().mul(lightSpecular)); | ||||
| 		 | ||||
| 		if (material.reflective().notZero()) { | ||||
| 			// When material has reflective properties we recursively find the color visible along the ray (p, r). | ||||
| 			Color lightReflected = sample(Ray.pd(p, r), depthRemaining - 1); | ||||
| 			result = result.add(material.reflective().mul(lightReflected)); | ||||
| 		} | ||||
| 
 | ||||
| 		double refractive = material.refractive().at(wavelength); | ||||
| 		if (refractive != 0.0) { | ||||
| 
 | ||||
| 		if (material.refractive().notZero()) { | ||||
| 			Vec3 b; // refracted light vector | ||||
| 			double rInd = 1/material.refractiveIndex().at(wavelength); | ||||
| 			double rInd = 1/material.refractiveIndex(); | ||||
| 
 | ||||
| 			double iCosN = i.dot(n_); | ||||
| 			if (iCosN < 0) { | ||||
|  | @ -114,10 +98,11 @@ public class RayTracerSimple extends RayTracer { | |||
| 				} | ||||
| 				b = bRejection.add(bProjection); | ||||
| 			} | ||||
| 			lightRefracted = sample(Ray.pd(p, b), wavelength, depthRemaining - 1); | ||||
| 			result += refractive * lightRefracted; | ||||
| 			Color lightRefracted = sample(Ray.pd(p, b), depthRemaining - 1); | ||||
| 			result = result.add(material.refractive().mul(lightRefracted)); | ||||
| 		} | ||||
| 		 | ||||
| 		return returnFactor * result; | ||||
| 		return result; | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,7 +1,5 @@ | |||
| /* | ||||
| package xyz.marsavic.gfxlab.graphics3d.scene; | ||||
| 
 | ||||
| 
 | ||||
| import xyz.marsavic.gfxlab.Color; | ||||
| import xyz.marsavic.gfxlab.Vec3; | ||||
| import xyz.marsavic.gfxlab.graphics3d.Light; | ||||
|  | @ -58,4 +56,3 @@ public class DiscoRoom extends Scene.Base { | |||
| 	} | ||||
| 	 | ||||
| } | ||||
| */ | ||||
|  | @ -1,4 +1,3 @@ | |||
| /* | ||||
| package xyz.marsavic.gfxlab.graphics3d.scene; | ||||
| 
 | ||||
| import xyz.marsavic.geometry.Vector; | ||||
|  | @ -51,4 +50,3 @@ public class Mirrors extends Scene.Base { | |||
| 	} | ||||
| 	 | ||||
| } | ||||
| */ | ||||
|  | @ -1,7 +1,5 @@ | |||
| /* | ||||
| package xyz.marsavic.gfxlab.graphics3d.scene; | ||||
| 
 | ||||
| 
 | ||||
| import xyz.marsavic.gfxlab.Color; | ||||
| import xyz.marsavic.gfxlab.Vec3; | ||||
| import xyz.marsavic.gfxlab.graphics3d.Light; | ||||
|  | @ -50,4 +48,3 @@ public class RefractionTest extends Scene.Base { | |||
| 	} | ||||
| 	 | ||||
| } | ||||
| */ | ||||
|  | @ -1,4 +1,4 @@ | |||
| /* package xyz.marsavic.gfxlab.graphics3d.scene; | ||||
| package xyz.marsavic.gfxlab.graphics3d.scene; | ||||
| 
 | ||||
| import xyz.marsavic.gfxlab.Color; | ||||
| import xyz.marsavic.gfxlab.Vec3; | ||||
|  | @ -32,4 +32,3 @@ public class SceneTest1 extends Scene.Base{ | |||
| 	} | ||||
| 	 | ||||
| } | ||||
| */ | ||||
|  | @ -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,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]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -4,7 +4,6 @@ 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; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -34,22 +33,22 @@ public class Grid implements F1<Material, Vector> { | |||
| 	} | ||||
| 	 | ||||
| 	 | ||||
| 	public static Grid standard(Spectrum spectrum) { | ||||
| 	public static Grid standard(Color color) { | ||||
| 		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) | ||||
| 				Material.matte(color), | ||||
| 				Material.matte(color.mul(0.75)) | ||||
| 		); | ||||
| 	} | ||||
| 	 | ||||
| 
 | ||||
| 	public static Grid standardUnit(Spectrum spectrum) { | ||||
| 	public static Grid standardUnit(Color color) { | ||||
| 		return new Grid( | ||||
| 				Vector.UNIT_DIAGONAL, | ||||
| 				Vector.xy(1.0/64), | ||||
| 				Material.matte(spectrum), | ||||
| 				Material.matte(w -> spectrum.at(w) * 0.75) | ||||
| 				Material.matte(color), | ||||
| 				Material.matte(color.mul(0.75)) | ||||
| 		); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ 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.scene.RefractionTest; | ||||
| import xyz.marsavic.gfxlab.gui.UtilsGL; | ||||
| import xyz.marsavic.gfxlab.tonemapping.ColorTransform; | ||||
| import xyz.marsavic.gfxlab.tonemapping.ToneMapping; | ||||
|  | @ -35,13 +35,14 @@ public class GfxLab { | |||
| 												e(RayTracerSimple::new, | ||||
| //														e(RefractionTest::new), | ||||
| //														e(DiscoRoom::new, val(16), val(16), val(0x3361EB272FEA4C62L)), | ||||
| 														e(SpectrumTest::new), | ||||
| 														e(RefractionTest::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))) | ||||
| //																	.then(Affine.rotationAboutY(0.03)) | ||||
| 															) | ||||
| 														) | ||||
| 												), | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue