Implement path tracer
This commit is contained in:
parent
bae05d4df5
commit
5dc461055e
|
@ -8,21 +8,23 @@ import xyz.marsavic.gfxlab.SplineSpectrum;
|
||||||
public record Material(
|
public record Material(
|
||||||
Spectrum diffuse,
|
Spectrum diffuse,
|
||||||
Spectrum specular,
|
Spectrum specular,
|
||||||
|
Spectrum emissive,
|
||||||
double shininess,
|
double shininess,
|
||||||
Spectrum reflective,
|
Spectrum reflective,
|
||||||
Spectrum refractive,
|
Spectrum refractive,
|
||||||
Spectrum refractiveIndex
|
Spectrum refractiveIndex
|
||||||
|
|
||||||
) {
|
) {
|
||||||
public Material diffuse (Spectrum diffuse ) { return new Material(diffuse, specular, shininess, reflective, refractive, 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, shininess, reflective, refractive, refractiveIndex); }
|
public Material specular (Spectrum specular ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
|
||||||
public Material shininess (double shininess ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); }
|
public Material emissive (Spectrum emissive ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
|
||||||
public Material reflective (Spectrum reflective ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); }
|
public Material shininess (double shininess ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
|
||||||
public Material refractive (Spectrum refractive ) { return new Material(diffuse, specular, shininess, reflective, refractive, refractiveIndex); }
|
public Material reflective (Spectrum reflective ) { return new Material(diffuse, specular, emissive, shininess, reflective, refractive, refractiveIndex); }
|
||||||
public Material refractiveIndex(Spectrum refractiveIndex) { return new Material(diffuse, specular, 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
|
// 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, 32, w -> 0, w -> 0, w -> 1.5);
|
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 (Spectrum s) { return BLACK.diffuse(s); }
|
||||||
public static Material matte (double k) { return matte(w -> k); }
|
public static Material matte (double k) { return matte(w -> k); }
|
||||||
|
@ -39,4 +41,6 @@ public record Material(
|
||||||
public static final Material GLASS = BLACK.refractive(w -> 1.0)
|
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
|
.refractiveIndex(w -> 1.6 + (w-400)/(800-400) * (1.55 - 1.6)); /* Made to roughly resemble refractive index
|
||||||
of BaK4 crown glass*/
|
of BaK4 crown glass*/
|
||||||
|
|
||||||
|
public static Material light (Spectrum s) { return BLACK.emissive(s); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package xyz.marsavic.gfxlab.graphics3d.raytracers;
|
package xyz.marsavic.gfxlab.graphics3d.raytracers;
|
||||||
|
|
||||||
|
import xyz.marsavic.geometry.Vec;
|
||||||
import xyz.marsavic.gfxlab.Color;
|
import xyz.marsavic.gfxlab.Color;
|
||||||
import xyz.marsavic.gfxlab.Vec3;
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
import xyz.marsavic.gfxlab.graphics3d.*;
|
import xyz.marsavic.gfxlab.graphics3d.*;
|
||||||
import xyz.marsavic.random.RNG;
|
import xyz.marsavic.random.RNG;
|
||||||
|
import xyz.marsavic.utils.Numeric;
|
||||||
|
|
||||||
public class RayTracerSimple extends RayTracer {
|
public class RayTracerSimple extends RayTracer {
|
||||||
|
|
||||||
|
@ -11,6 +13,8 @@ public class RayTracerSimple extends RayTracer {
|
||||||
private static final double minWavelength = 380;
|
private static final double minWavelength = 380;
|
||||||
private static final double maxWavelength = 780;
|
private static final double maxWavelength = 780;
|
||||||
private static final double EPSILON = 1e-9;
|
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) {
|
public RayTracerSimple(Scene scene, Camera camera) {
|
||||||
super(scene, camera);
|
super(scene, camera);
|
||||||
|
@ -20,34 +24,31 @@ public class RayTracerSimple extends RayTracer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Color sample(Ray ray) {
|
protected Color sample(Ray ray) {
|
||||||
Color s = Color.BLACK;
|
Color result = Color.BLACK;
|
||||||
int n = 5;
|
for (int i = 0; i < sampleNumber; i++) {
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
s = s.add(sample(ray, 64));
|
|
||||||
}
|
|
||||||
return s.div(n);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Color sample(Ray ray, int depthRemaining) {
|
|
||||||
double x = 0.0, y = 0.0, z = 0.0;
|
double x = 0.0, y = 0.0, z = 0.0;
|
||||||
for (int i = 0; i < spectrumSamples; i++) {
|
for (int j = 0; j < spectrumSamples; j++) {
|
||||||
double wavelength = minWavelength + (((double) i + rng.nextDouble())/spectrumSamples)*(maxWavelength - minWavelength);
|
double wavelength = minWavelength + (((double) j + rng.nextDouble()) / spectrumSamples) * (maxWavelength - minWavelength);
|
||||||
double intensity = sample(ray, depthRemaining, wavelength);
|
double intensity = sample(ray, wavelength, 7);
|
||||||
|
|
||||||
x += intensity * Xyz.x[(int) wavelength];
|
x += intensity * Xyz.x[(int) wavelength];
|
||||||
y += intensity * Xyz.y[(int) wavelength];
|
y += intensity * Xyz.y[(int) wavelength];
|
||||||
z += intensity * Xyz.z[(int) wavelength];
|
z += intensity * Xyz.z[(int) wavelength];
|
||||||
}
|
}
|
||||||
|
|
||||||
Color result = Color.xyz(x,y,z);
|
result = result.add(Color.xyz(x, y, z));
|
||||||
|
}
|
||||||
return result;
|
return result.div(sampleNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected double sample(Ray ray, int depthRemaining, double wavelength) {
|
protected double sample(Ray ray, double wavelength, int depthRemaining) {
|
||||||
if (depthRemaining == 0) {
|
double returnFactor = 1.0;
|
||||||
|
if (depthRemaining <= 0) {
|
||||||
|
if (rng.nextDouble(1.0) <= stopProbability) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
} else {
|
||||||
|
returnFactor = 1/(1-stopProbability);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Hit hit = scene.solid().firstHit(ray, EPSILON);
|
Hit hit = scene.solid().firstHit(ray, EPSILON);
|
||||||
|
@ -62,44 +63,31 @@ public class RayTracerSimple extends RayTracer {
|
||||||
Vec3 r = GeometryUtils.reflectedN(n_, i); // Reflected ray (i reflected over n)
|
Vec3 r = GeometryUtils.reflectedN(n_, i); // Reflected ray (i reflected over n)
|
||||||
Vec3 r_ = r.div(lI); // Reflected ray (i reflected over n)
|
Vec3 r_ = r.div(lI); // Reflected ray (i reflected over n)
|
||||||
|
|
||||||
double lightDiffuse = 0.0; // The sum of diffuse contributions from all the lights
|
|
||||||
double lightSpecular = 0.0; // The sum of specular contributions from all the lights
|
|
||||||
|
|
||||||
Material material = hit.material();
|
Material material = hit.material();
|
||||||
|
|
||||||
for (Light light : scene.lights()) {
|
double lightDiffuse = 0.0; // The diffuse contribution of the generated path
|
||||||
Vec3 l = light.p().sub(p); // Vector from p to the light;
|
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
|
||||||
|
|
||||||
Ray rayToLight = Ray.pd(p, l);
|
double result = lightEmissive;
|
||||||
if (scene.solid().hitBetween(rayToLight, EPSILON, 1)) continue;
|
|
||||||
|
|
||||||
double lLSqr = l.lengthSquared(); // Distance from p to the light squared
|
double diffuse = material.diffuse().at(wavelength);
|
||||||
double lL = Math.sqrt(lLSqr); // Distance from p to the light
|
if (diffuse != 0.0) {
|
||||||
double cosLN = n_.dot(l) / lL; // Cosine of the angle between l and n_
|
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_
|
||||||
if (cosLN > 0) { // If the light is above the surface
|
Vec3 u_ = GeometryUtils.normal(n_).normalized_(); // One of the normalized vectors normal to n
|
||||||
double irradiance = light.s().at(wavelength) * cosLN / lLSqr;
|
Vec3 v_ = n_.cross(u_); // Doesn't need to be normalized because n_ and u_ are normalized and normal
|
||||||
// The irradiance represents how much light is received by a unit area of the surface. It is
|
Vec3 o = u_.mul(Numeric.sinT(r1)).add(v_.mul(Numeric.cosT(r1)))
|
||||||
// proportional to the cosine of the incoming angle and inversely proportional to the distance squared
|
.mul(Numeric.cosT(r2)).add(n_.mul(Numeric.sinT(r2))); // Outgoing sample vector
|
||||||
// (inverse-square law).
|
lightDiffuse = diffuse * sample(Ray.pd(p, o), wavelength, depthRemaining - 1);
|
||||||
lightDiffuse = lightDiffuse+irradiance;
|
|
||||||
|
|
||||||
double cosLR = l.dot(r_);
|
|
||||||
if (cosLR > 0) { // If the angle between l and r is acute
|
|
||||||
cosLR /= lL;
|
|
||||||
lightSpecular = lightSpecular + irradiance * Math.pow(cosLR, material.shininess());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double result = 0.0;
|
result += lightDiffuse;
|
||||||
result += material.diffuse().at(wavelength) * lightDiffuse;
|
|
||||||
result += material.specular().at(wavelength) * lightSpecular;
|
|
||||||
|
|
||||||
double reflective = material.reflective().at(wavelength);
|
double reflective = material.reflective().at(wavelength);
|
||||||
if (reflective != 0.0) {
|
if (reflective != 0.0) {
|
||||||
// When material has reflective properties we recursively find the color visible along the ray (p, r).
|
lightReflected = sample(Ray.pd(p, r), wavelength, depthRemaining - 1);
|
||||||
double lightReflected = sample(Ray.pd(p, r), depthRemaining - 1, wavelength);
|
|
||||||
result += reflective * lightReflected;
|
result += reflective * lightReflected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,10 +114,10 @@ public class RayTracerSimple extends RayTracer {
|
||||||
}
|
}
|
||||||
b = bRejection.add(bProjection);
|
b = bRejection.add(bProjection);
|
||||||
}
|
}
|
||||||
double lightRefracted = sample(Ray.pd(p, b), depthRemaining - 1, wavelength);
|
lightRefracted = sample(Ray.pd(p, b), wavelength, depthRemaining - 1);
|
||||||
result += refractive * lightRefracted;
|
result += refractive * lightRefracted;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return returnFactor * result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
package xyz.marsavic.gfxlab.graphics3d.scene;
|
package xyz.marsavic.gfxlab.graphics3d.scene;
|
||||||
|
|
||||||
|
|
||||||
import javafx.util.Pair;
|
|
||||||
import xyz.marsavic.functions.interfaces.F1;
|
import xyz.marsavic.functions.interfaces.F1;
|
||||||
import xyz.marsavic.geometry.Vector;
|
import xyz.marsavic.geometry.Vector;
|
||||||
import xyz.marsavic.gfxlab.Color;
|
|
||||||
import xyz.marsavic.gfxlab.Spectrum;
|
|
||||||
import xyz.marsavic.gfxlab.SplineSpectrum;
|
import xyz.marsavic.gfxlab.SplineSpectrum;
|
||||||
import xyz.marsavic.gfxlab.Vec3;
|
import xyz.marsavic.gfxlab.Vec3;
|
||||||
import xyz.marsavic.gfxlab.graphics3d.Light;
|
import xyz.marsavic.gfxlab.graphics3d.*;
|
||||||
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.Ball;
|
||||||
import xyz.marsavic.gfxlab.graphics3d.solids.Group;
|
import xyz.marsavic.gfxlab.graphics3d.solids.Group;
|
||||||
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
|
import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace;
|
||||||
|
import xyz.marsavic.gfxlab.graphics3d.solids.Parallelepiped;
|
||||||
import xyz.marsavic.gfxlab.graphics3d.textures.Grid;
|
import xyz.marsavic.gfxlab.graphics3d.textures.Grid;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -25,28 +20,42 @@ public class SpectrumTest extends Scene.Base {
|
||||||
|
|
||||||
public SpectrumTest() {
|
public SpectrumTest() {
|
||||||
var materialUVWalls = (F1<Material, Vector>) (uv -> Material.matte(1.0));
|
var materialUVWalls = (F1<Material, Vector>) (uv -> Material.matte(1.0));
|
||||||
SplineSpectrum s;
|
var materialBlocks = (F1<Material, Vector>) (uv -> Material.matte(0.0));
|
||||||
var materialUVWallsL = Grid.standard(w -> 1.0);
|
var materialUVWallsL = Grid.standard(w -> 1.0);
|
||||||
|
var materialUVWallsB = Grid.standard(w -> 1.0);
|
||||||
|
|
||||||
var materialUVWallsR = Grid.standard(w -> 0.1);
|
var materialUVWallsR = (F1<Material, Vector>) (uv -> Material.MIRROR);
|
||||||
|
|
||||||
|
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<>();
|
Collection<Solid> solids = new ArrayList<>();
|
||||||
Collections.addAll(solids,
|
Collections.addAll(solids,
|
||||||
HalfSpace.pn(Vec3.xyz(1, 0, 10), Vec3.xyz( 1, 0, -1), materialUVWallsL),
|
HalfSpace.pn(Vec3.xyz(-1, 0, 0), Vec3.xyz( 1, 0, 0), materialUVWallsL),
|
||||||
HalfSpace.pn(Vec3.xyz( 0, 0, 10), Vec3.xyz(-1, 0, -1), materialUVWallsR),
|
HalfSpace.pn(Vec3.xyz( 1, 0, 0), Vec3.xyz(-1, 0, 0), materialUVWallsR),
|
||||||
HalfSpace.pn(Vec3.xyz(0, 2, 0), Vec3.xyz( 0, -1, 0), materialUVWallsL),
|
HalfSpace.pn(Vec3.xyz( 0, -1, 0), Vec3.xyz( 0, 1, 0), materialUVWalls),
|
||||||
HalfSpace.pn(Vec3.xyz(0, -2, 10), Vec3.xyz( 0, 1, 0), materialUVWallsL),
|
// 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, 8), Vec3.xyz(0.3, 1, -1), uv -> Material.GLASS.refractiveIndex(
|
HalfSpace.pn(Vec3.xyz( 0, 0, -6), Vec3.xyz( 0, 0, 1), materialUVWallsB),
|
||||||
w -> 5.6 + (w-400)/(800-400) * (1.55 - 5.6)
|
Ball.cr(Vec3.xyz(0, 3, 0), 0.8, materialLight),
|
||||||
|
Parallelepiped.pabc(Vec3.xyz(0, 0, 0),
|
||||||
)),
|
Vec3.xyz(0.4, 0, 0),
|
||||||
HalfSpace.pn(Vec3.xyz( 0, 0, -6), Vec3.xyz( 0, 0, 1), materialUVWallsR)
|
Vec3.xyz(0, 0.4, 0),
|
||||||
);
|
Vec3.xyz(0, 0, 6),
|
||||||
|
materialGlass),
|
||||||
Collections.addAll(lights,
|
Parallelepiped.pabc(Vec3.xyz(-5.5 , 1, 1),
|
||||||
Light.ps(Vec3.xyz(0, 1.0, -1.0), Spectrum.WHITE),
|
Vec3.xyz(4.5, 0, 0),
|
||||||
Light.ps(Vec3.xyz(0, -1.0, 8.0), Spectrum.WHITE)
|
Vec3.xyz(0, 0.4, 0),
|
||||||
|
Vec3.xyz(0, 0, -6),
|
||||||
|
materialGlass),
|
||||||
|
Parallelepiped.pabc(Vec3.xyz(0.5, 1, 1),
|
||||||
|
Vec3.xyz(4.5, 0, 0),
|
||||||
|
Vec3.xyz(0, 0.4, 0),
|
||||||
|
Vec3.xyz(0, 0, -6),
|
||||||
|
materialGlass)
|
||||||
);
|
);
|
||||||
|
|
||||||
solid = Group.of(solids);
|
solid = Group.of(solids);
|
||||||
|
|
Loading…
Reference in a new issue