added diffuse pathtracing
This commit is contained in:
parent
3d3bc7bd95
commit
b882192fe7
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,124 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Palette2">
|
||||||
|
<group name="Swing">
|
||||||
|
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Button" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="RadioButton" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="CheckBox" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||||
|
<initial-values>
|
||||||
|
<property name="text" value="Label" />
|
||||||
|
</initial-values>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||||
|
<preferred-size width="150" height="-1" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||||
|
<preferred-size width="150" height="50" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||||
|
<preferred-size width="200" height="200" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||||
|
<preferred-size width="-1" height="20" />
|
||||||
|
</default-constraints>
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||||
|
</item>
|
||||||
|
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||||
|
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||||
|
</item>
|
||||||
|
</group>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,36 @@
|
||||||
|
package basics.math.algebra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basis of an vector space.
|
||||||
|
* This class owns the components of a vector space.
|
||||||
|
*/
|
||||||
|
public class Basis {
|
||||||
|
|
||||||
|
public Vector[] vectors;
|
||||||
|
|
||||||
|
public Basis(Vector[] vectors) {
|
||||||
|
this.vectors = vectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Basis(Vector a, Vector b, Vector c) {
|
||||||
|
this.vectors = new Vector[]{a, b, c};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an orthonormal basis using the gram-schmidt formula.
|
||||||
|
* It will utilize the supplied up vector as the first basis vector,
|
||||||
|
* building the other two vectors from swizzling the up vectors components.
|
||||||
|
*
|
||||||
|
* @param up the first vector of this basis
|
||||||
|
* @return an orthonormal basis with up and 2 additional orthonormal vectors
|
||||||
|
*/
|
||||||
|
public static Basis constructOrthonormalBasis(Vector up) {
|
||||||
|
Vector n2 = Vector.diagonal(1).sub(up);
|
||||||
|
Vector n3 = Vector.diagonal(1).sub(up.shuffle(Vector.SwizzleMask.ZXY));
|
||||||
|
|
||||||
|
Vector w2 = n2.sub(up.project(n2)).normalize();
|
||||||
|
Vector w3 = n3.sub(up.project(n3)).sub(w2.project(n3)).normalize();
|
||||||
|
|
||||||
|
return new Basis(up, w2, w3);
|
||||||
|
}
|
||||||
|
}
|
|
@ -96,6 +96,10 @@ public class Vector {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector project(Vector other) {
|
||||||
|
return this.scale(other.dot(this) / this.dot(this));
|
||||||
|
}
|
||||||
|
|
||||||
public Vector reflect(Vector normal) {
|
public Vector reflect(Vector normal) {
|
||||||
return this.sub(normal.scale(this.dot(normal) * 2.0));
|
return this.sub(normal.scale(this.dot(normal) * 2.0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
package entry;
|
package entry;
|
||||||
|
|
||||||
import optics.view.PlayerController;
|
import optics.view.PlayerController;
|
||||||
import raytracing.Raytracer;
|
import pathtracing.Pathtracer;
|
||||||
import renderer.Display;
|
import renderer.Display;
|
||||||
|
import renderer.Renderer;
|
||||||
import renderer.Resolution;
|
import renderer.Resolution;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
Resolution resolution = new Resolution(800, 800, 3);
|
Resolution resolution = new Resolution(800, 800, 6);
|
||||||
|
|
||||||
PlayerController controller = new PlayerController();
|
PlayerController controller = new PlayerController();
|
||||||
|
|
||||||
Display display = new Display(resolution, controller);
|
Display display = new Display(resolution, controller);
|
||||||
|
|
||||||
Raytracer raytracer = new Raytracer();
|
Renderer raytracer = new Pathtracer();
|
||||||
|
|
||||||
display.start(raytracer, controller);
|
display.start(raytracer, controller);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import optics.light.Color;
|
||||||
import optics.light.Directional;
|
import optics.light.Directional;
|
||||||
import optics.light.LightSource;
|
import optics.light.LightSource;
|
||||||
import optics.light.Point;
|
import optics.light.Point;
|
||||||
|
import pathtracing.BSDF;
|
||||||
import raytracing.BasicMaterial;
|
import raytracing.BasicMaterial;
|
||||||
import renderer.mesh.BasicMesh;
|
import renderer.mesh.BasicMesh;
|
||||||
import renderer.mesh.Mesh;
|
import renderer.mesh.Mesh;
|
||||||
|
@ -29,7 +30,7 @@ public class Scene {
|
||||||
this.lights = new ArrayList<>();
|
this.lights = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Scene generateExampleScene() {
|
public static Scene generateRaytracingExampleScene() {
|
||||||
Scene scene = new Scene(new LinearList());
|
Scene scene = new Scene(new LinearList());
|
||||||
|
|
||||||
BasicMaterial glass = new BasicMaterial(Color.BLACK, 1.0, Color.WHITE, true);
|
BasicMaterial glass = new BasicMaterial(Color.BLACK, 1.0, Color.WHITE, true);
|
||||||
|
@ -58,6 +59,33 @@ public class Scene {
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Scene generatePathtracingExampleScene() {
|
||||||
|
Scene scene = new Scene(new LinearList());
|
||||||
|
|
||||||
|
BSDF red = new BSDF(new Color(1, 0.3, 0.3), new Color(0), 1.0);
|
||||||
|
BSDF green = new BSDF(new Color(0.3,1, 0.3), new Color(0), 1);
|
||||||
|
BSDF blue = new BSDF(new Color(0.3, 0.3, 1), new Color(0), 1);
|
||||||
|
BSDF white = new BSDF(new Color(1, 1, 1), new Color(0), 1);
|
||||||
|
BSDF light = new BSDF(Color.BLACK, new Color(300), 1.0);
|
||||||
|
|
||||||
|
scene.addMesh(new BasicMesh(white, new Sphere(new Vector(0,0,0), 1.0)));
|
||||||
|
scene.addMesh(new BasicMesh(white, new Sphere(new Vector(3,2,1), 1.0)));
|
||||||
|
|
||||||
|
scene.addMesh(new BasicMesh(white, new Plane(new Vector(0, 1, 0), 1)));
|
||||||
|
scene.addMesh(new BasicMesh(white, new Plane(new Vector(0, 1, 0), -6)));
|
||||||
|
|
||||||
|
scene.addMesh(new BasicMesh(red, new Plane(new Vector(-1, 0, 0), 4)));
|
||||||
|
scene.addMesh(new BasicMesh(green, new Plane(new Vector(-1, 0, 0), -4)));
|
||||||
|
|
||||||
|
scene.addMesh(new BasicMesh(blue, new Plane(new Vector(0, 0, 1), 5)));
|
||||||
|
scene.addMesh(new BasicMesh(white, new Plane(new Vector(0, 0, 1), -4)));
|
||||||
|
|
||||||
|
scene.addMesh(new BasicMesh(light, new Sphere(new Vector(0,6.0,0), 1.0)));
|
||||||
|
//scene.addLight(new Point(Color.WHITE.scale(1.0), new Vector(0, 4, 0)));
|
||||||
|
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
public void addLight(LightSource light) {
|
public void addLight(LightSource light) {
|
||||||
this.lights.add(light);
|
this.lights.add(light);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,24 @@ public class Color {
|
||||||
return this.scale(k).add(other.scale(1.0 - k));
|
return this.scale(k).add(other.scale(1.0 - k));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void colorCorrect() {
|
||||||
|
this.r = tonemap(this.r);
|
||||||
|
this.g = tonemap(this.g);
|
||||||
|
this.b = tonemap(this.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double tonemap(double k) {
|
||||||
|
return -1 / (k + 1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIntRGB() {
|
||||||
|
int r = (int) (this.r * 255);
|
||||||
|
int g = (int) (this.g * 255);
|
||||||
|
int b = (int) (this.b * 255);
|
||||||
|
|
||||||
|
return 0xff000000 | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
|
||||||
public enum SwizzleMask {
|
public enum SwizzleMask {
|
||||||
rgb,
|
rgb,
|
||||||
grb,
|
grb,
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package pathtracing;
|
||||||
|
|
||||||
|
import optics.light.Color;
|
||||||
|
import shading.Material;
|
||||||
|
|
||||||
|
public class BSDF implements Material {
|
||||||
|
|
||||||
|
private Color reflectance;
|
||||||
|
private Color emission;
|
||||||
|
|
||||||
|
private double roughness;
|
||||||
|
|
||||||
|
public BSDF(Color reflectance, Color emission, double roughness) {
|
||||||
|
this.reflectance = reflectance;
|
||||||
|
this.emission = emission;
|
||||||
|
this.roughness = roughness;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getReflectance() {
|
||||||
|
return reflectance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getEmission() {
|
||||||
|
return emission;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRoughness() {
|
||||||
|
return roughness;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
package pathtracing;
|
||||||
|
|
||||||
|
import basics.math.algebra.Basis;
|
||||||
|
import basics.math.algebra.Ray;
|
||||||
|
import basics.math.algebra.Vector;
|
||||||
|
import geometry.scene.Hit;
|
||||||
|
import geometry.scene.Scene;
|
||||||
|
import optics.light.Color;
|
||||||
|
import optics.light.LightSource;
|
||||||
|
import optics.view.Camera;
|
||||||
|
import optics.view.PinholeCamera;
|
||||||
|
import renderer.Renderer;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class Pathtracer implements Renderer {
|
||||||
|
|
||||||
|
private final Camera camera = new PinholeCamera(new Vector(0,0,-4), Vector.origin(), 90, 1e-3, 1e3);
|
||||||
|
private final Scene scene = Scene.generatePathtracingExampleScene();
|
||||||
|
|
||||||
|
private double traceShadow(Vector point, Vector direction, double distance) {
|
||||||
|
Ray shadowRay = new Ray(point, direction, 1e-3, distance);
|
||||||
|
|
||||||
|
Optional<Hit> result = scene.intersect(shadowRay);
|
||||||
|
|
||||||
|
if (result.isEmpty()) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double castShadow(LightSource target, Vector point, Vector surfaceNormal) {
|
||||||
|
Vector lightDirection = target.getDirection(point);
|
||||||
|
double distance = target.getDistance(point);
|
||||||
|
double shadow = traceShadow(point, lightDirection, distance);
|
||||||
|
return Math.max(surfaceNormal.dot(lightDirection), 0.0) * shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color traceDirect(Ray directRay, int depth) {
|
||||||
|
if (depth == 8) {
|
||||||
|
return Color.BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Hit> result = scene.intersect(directRay);
|
||||||
|
|
||||||
|
if (result.isEmpty()) {
|
||||||
|
// Background color
|
||||||
|
return Color.BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector intersection = directRay.travel(result.get().getDistance());
|
||||||
|
Vector normal = result.get().getMesh().normalAt(intersection, directRay.getDirection());
|
||||||
|
|
||||||
|
BSDF material = (BSDF) result.get().getMesh().getMaterial();
|
||||||
|
|
||||||
|
var diffuse = diffuseCosineWeightedBRDF(intersection, normal, material, depth);
|
||||||
|
|
||||||
|
return diffuse.add(material.getEmission());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color diffuseCosineWeightedBRDF(Vector intersection, Vector normal, BSDF bsdf, int depth) {
|
||||||
|
var next = new Ray(intersection, sampleNormalHemisphere(normal, bsdf.getRoughness()), 1e-3, 1e3);
|
||||||
|
|
||||||
|
var cosTheta = next.getDirection().dot(normal);
|
||||||
|
|
||||||
|
var pdf = cosTheta / Math.PI;
|
||||||
|
|
||||||
|
var indirectLight = traceDirect(next, depth + 1).scale(pdf);
|
||||||
|
|
||||||
|
// direct light
|
||||||
|
|
||||||
|
Color directLight = Color.BLACK;
|
||||||
|
|
||||||
|
for (LightSource light : scene.getLights()) {
|
||||||
|
double directInfluence = castShadow(light, intersection, normal);
|
||||||
|
|
||||||
|
Color incoming = bsdf.getReflectance().scale(directInfluence).mul(light.getColor());
|
||||||
|
|
||||||
|
directLight = directLight.add(incoming);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bsdf.getReflectance().mul(indirectLight.add(directLight));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector cosineHemisphere() {
|
||||||
|
double u1 = Math.random();
|
||||||
|
double u2 = Math.random();
|
||||||
|
|
||||||
|
double r = Math.sqrt(u1);
|
||||||
|
double theta = 2 * Math.PI * u2;
|
||||||
|
|
||||||
|
double x = r * Math.cos(theta);
|
||||||
|
double y = r * Math.sin(theta);
|
||||||
|
|
||||||
|
return new Vector(x, y, Math.sqrt(1 - u1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector sampleNormalHemisphere(Vector normal, double roughness) {
|
||||||
|
Vector hemisphereSample = cosineHemisphere();
|
||||||
|
|
||||||
|
var orthnormalBasis = Basis.constructOrthonormalBasis(normal);
|
||||||
|
|
||||||
|
return orthnormalBasis.vectors[0].scale(hemisphereSample.z)
|
||||||
|
.add(orthnormalBasis.vectors[1].scale(hemisphereSample.x))
|
||||||
|
.add(orthnormalBasis.vectors[2].scale(hemisphereSample.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color traceCameraRay(double u, double v) {
|
||||||
|
var cameraRay = camera.generateViewRay(u, v);
|
||||||
|
|
||||||
|
return traceDirect(cameraRay, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Camera getCamera() {
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,8 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Raytracer implements Renderer {
|
public class Raytracer implements Renderer {
|
||||||
|
|
||||||
private Camera camera = new PinholeCamera(new Vector(0,0,-4), Vector.origin(), 90, 1e-3, 1e3);
|
private final Camera camera = new PinholeCamera(new Vector(0,0,-4), Vector.origin(), 90, 1e-3, 1e3);
|
||||||
private Scene scene = Scene.generateExampleScene();
|
private final Scene scene = Scene.generateRaytracingExampleScene();
|
||||||
|
|
||||||
private double traceShadow(Vector point, Vector direction, double distance) {
|
private double traceShadow(Vector point, Vector direction, double distance) {
|
||||||
Ray shadowRay = new Ray(point, direction, 1e-3, distance);
|
Ray shadowRay = new Ray(point, direction, 1e-3, distance);
|
||||||
|
@ -50,7 +50,12 @@ public class Raytracer implements Renderer {
|
||||||
Color incoming = new Color(0,0,0);
|
Color incoming = new Color(0,0,0);
|
||||||
|
|
||||||
if (material.refracts()) {
|
if (material.refracts()) {
|
||||||
return refractedLight(directRay, material, normal, intersection, depth);
|
var reflection = reflectedLight(directRay, material, normal, intersection, depth);
|
||||||
|
var refraction = refractedLight(directRay, material, normal, intersection, depth);
|
||||||
|
|
||||||
|
var fac = -directRay.getDirection().dot(normal);
|
||||||
|
|
||||||
|
return refraction.lerp(reflection, Math.pow(fac, 0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
Color reflectedLight = reflectedLight(directRay, material, normal, intersection, depth);
|
Color reflectedLight = reflectedLight(directRay, material, normal, intersection, depth);
|
||||||
|
@ -77,9 +82,7 @@ public class Raytracer implements Renderer {
|
||||||
private Color refractedLight(Ray incomingRay, BasicMaterial material, Vector normal, Vector point, int depth) {
|
private Color refractedLight(Ray incomingRay, BasicMaterial material, Vector normal, Vector point, int depth) {
|
||||||
Vector refracted = incomingRay.getDirection().refract(normal, incomingRay.getDirection(), 1.45);
|
Vector refracted = incomingRay.getDirection().refract(normal, incomingRay.getDirection(), 1.45);
|
||||||
|
|
||||||
Color refractedLight = traceDirect(new Ray(point, refracted, 1e-3, incomingRay.getFar()), depth + 1);
|
return traceDirect(new Ray(point, refracted, 1e-3, incomingRay.getFar()), depth + 1);
|
||||||
|
|
||||||
return refractedLight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double castShadow(LightSource target, Vector point, Vector surfaceNormal) {
|
private double castShadow(LightSource target, Vector point, Vector surfaceNormal) {
|
||||||
|
|
|
@ -4,9 +4,12 @@ import optics.light.Color;
|
||||||
import optics.view.PlayerController;
|
import optics.view.PlayerController;
|
||||||
import renderer.canvas.RenderTarget;
|
import renderer.canvas.RenderTarget;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class Display {
|
public class Display {
|
||||||
|
|
||||||
|
@ -19,6 +22,7 @@ public class Display {
|
||||||
|
|
||||||
private int sampleCount;
|
private int sampleCount;
|
||||||
private long frametime;
|
private long frametime;
|
||||||
|
private long startTime;
|
||||||
|
|
||||||
public Display(Resolution resolution, PlayerController controller) {
|
public Display(Resolution resolution, PlayerController controller) {
|
||||||
this.resolution = resolution;
|
this.resolution = resolution;
|
||||||
|
@ -86,9 +90,16 @@ public class Display {
|
||||||
try (Scheduler scheduler = Scheduler.getInstance()) {
|
try (Scheduler scheduler = Scheduler.getInstance()) {
|
||||||
var tasks = scheduler.generateTasks(this.target, program);
|
var tasks = scheduler.generateTasks(this.target, program);
|
||||||
|
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
while (!this.close) {
|
while (!this.close) {
|
||||||
this.frametime = System.currentTimeMillis();
|
this.frametime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() - startTime > 1000 * 60 * 20) {
|
||||||
|
ImageIO.write(this.target.getImage(), "png", new File("images/" + System.currentTimeMillis() + ".png"));
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (resized) {
|
if (resized) {
|
||||||
this.resolution.setSize(window.getSize());
|
this.resolution.setSize(window.getSize());
|
||||||
// Window has been resized, rebuild drawing buffer
|
// Window has been resized, rebuild drawing buffer
|
||||||
|
@ -128,5 +139,6 @@ public class Display {
|
||||||
overlay.setThreads();
|
overlay.setThreads();
|
||||||
overlay.setFrametime(this.frametime);
|
overlay.setFrametime(this.frametime);
|
||||||
overlay.setSamples(sampleCount);
|
overlay.setSamples(sampleCount);
|
||||||
|
overlay.setTime(System.currentTimeMillis() - startTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
package renderer;
|
package renderer;
|
||||||
|
|
||||||
import basics.math.algebra.Vector;
|
|
||||||
import renderer.canvas.ContributionBuffer;
|
import renderer.canvas.ContributionBuffer;
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
public class TileProcessor implements Callable<Void> {
|
public class TileProcessor implements Callable<Void> {
|
||||||
|
|
||||||
private final ContributionBuffer buffer;
|
private final ContributionBuffer buffer;
|
||||||
|
|
||||||
private FragmentProgram program;
|
private final FragmentProgram program;
|
||||||
|
|
||||||
private int width;
|
private final int width;
|
||||||
private int height;
|
private final int height;
|
||||||
private int x0;
|
private final int x0;
|
||||||
private int y0;
|
private final int y0;
|
||||||
private int x1;
|
private final int x1;
|
||||||
private int y1;
|
private final int y1;
|
||||||
|
|
||||||
public TileProcessor(ContributionBuffer buffer, int x0, int y0, int x1, int y1, int width, int height, FragmentProgram program) {
|
public TileProcessor(ContributionBuffer buffer, int x0, int y0, int x1, int y1, int width, int height, FragmentProgram program) {
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
|
@ -30,13 +28,15 @@ public class TileProcessor implements Callable<Void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void call() throws Exception {
|
public Void call() {
|
||||||
|
double widthTransform = 1.0 / (double) this.width * 2.0;
|
||||||
|
double heightTransform = 1.0 / (double) this.height * 2.0;
|
||||||
|
|
||||||
for (int x = this.x0; x < x1; x++) {
|
for (int x = this.x0; x < x1; x++) {
|
||||||
double u = (x + Math.random()) / (double) this.width * 2.0 - 1.0;
|
double u = (x + Math.random()) * widthTransform - 1.0;
|
||||||
|
|
||||||
for (int y = this.y0; y < y1; y++) {
|
for (int y = this.y0; y < y1; y++) {
|
||||||
double v = (y + Math.random()) / (double) this.height * 2.0 - 1.0;
|
double v = (y + Math.random()) * heightTransform - 1.0;
|
||||||
|
|
||||||
this.buffer.contribute(x, y, this.program.fragment(u, v));
|
this.buffer.contribute(x, y, this.program.fragment(u, v));
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ public class ContributionBuffer {
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
|
|
||||||
|
private int maxSample;
|
||||||
|
|
||||||
public ContributionBuffer(int width, int height) {
|
public ContributionBuffer(int width, int height) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
|
@ -30,6 +32,8 @@ public class ContributionBuffer {
|
||||||
public void contribute(int x, int y, Color color) {
|
public void contribute(int x, int y, Color color) {
|
||||||
buffer[x][y] = buffer[x][y].add(color);
|
buffer[x][y] = buffer[x][y].add(color);
|
||||||
samples[x][y]++;
|
samples[x][y]++;
|
||||||
|
|
||||||
|
maxSample = Math.max(maxSample, (int) samples[x][y]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,6 +55,8 @@ public class ContributionBuffer {
|
||||||
private int getRGB(int x, int y) {
|
private int getRGB(int x, int y) {
|
||||||
Color linearRGB = buffer[x][y].scale(1.0 / samples[x][y]);
|
Color linearRGB = buffer[x][y].scale(1.0 / samples[x][y]);
|
||||||
|
|
||||||
|
linearRGB.colorCorrect();
|
||||||
|
|
||||||
int red = (int) (linearRGB.r* 255.0);
|
int red = (int) (linearRGB.r* 255.0);
|
||||||
int green = (int) (linearRGB.g * 255.0);
|
int green = (int) (linearRGB.g * 255.0);
|
||||||
int blue = (int) (linearRGB.b * 255.0);
|
int blue = (int) (linearRGB.b * 255.0);
|
||||||
|
@ -62,6 +68,20 @@ public class ContributionBuffer {
|
||||||
return 0xff000000 | red << 16 | green << 8 | blue;
|
return 0xff000000 | red << 16 | green << 8 | blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Color getPixel(int x, int y) {
|
||||||
|
return buffer[x][y].scale(1.0 / samples[x][y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visualizeSamples(int[] buffer) {
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
int col = y * width;
|
||||||
|
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
buffer[col + x] = Color.diagonal(samples[x][y] / maxSample).getIntRGB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void blit(int[] buffer) {
|
public void blit(int[] buffer) {
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
int col = y * width;
|
int col = y * width;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package renderer.canvas;
|
package renderer.canvas;
|
||||||
|
|
||||||
import optics.view.PlayerController;
|
|
||||||
import renderer.Resolution;
|
import renderer.Resolution;
|
||||||
import renderer.Scheduler;
|
import renderer.Scheduler;
|
||||||
import renderer.debug.DebugOverlay;
|
import renderer.debug.DebugOverlay;
|
||||||
|
@ -9,6 +8,7 @@ import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.DataBufferInt;
|
import java.awt.image.DataBufferInt;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
|
||||||
public class RenderTarget extends JPanel {
|
public class RenderTarget extends JPanel {
|
||||||
|
|
||||||
|
@ -70,4 +70,8 @@ public class RenderTarget extends JPanel {
|
||||||
public DebugOverlay getOverlay() {
|
public DebugOverlay getOverlay() {
|
||||||
return overlay;
|
return overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RenderedImage getImage() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<properties/>
|
<properties/>
|
||||||
<border type="none"/>
|
<border type="none"/>
|
||||||
<children>
|
<children>
|
||||||
<grid id="eb7a2" layout-manager="GridLayoutManager" row-count="7" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
<grid id="eb7a2" layout-manager="GridLayoutManager" row-count="8" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
<margin top="0" left="8" bottom="0" right="8"/>
|
<margin top="0" left="8" bottom="0" right="8"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<grid row="0" column="0" row-span="9" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
<grid row="0" column="0" row-span="9" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
</component>
|
</component>
|
||||||
<vspacer id="8c764">
|
<vspacer id="8c764">
|
||||||
<constraints>
|
<constraints>
|
||||||
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</vspacer>
|
</vspacer>
|
||||||
<component id="4bbcc" class="javax.swing.JLabel">
|
<component id="4bbcc" class="javax.swing.JLabel">
|
||||||
|
@ -117,6 +117,22 @@
|
||||||
<text value="866"/>
|
<text value="866"/>
|
||||||
</properties>
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
|
<component id="f3105" class="javax.swing.JLabel" binding="time">
|
||||||
|
<constraints>
|
||||||
|
<grid row="6" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="Label"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="951eb" class="javax.swing.JLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="Time"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
</children>
|
</children>
|
||||||
</grid>
|
</grid>
|
||||||
<hspacer id="6cc64">
|
<hspacer id="6cc64">
|
||||||
|
|
|
@ -14,6 +14,7 @@ public class DebugOverlay {
|
||||||
private JLabel samples;
|
private JLabel samples;
|
||||||
private JPanel root;
|
private JPanel root;
|
||||||
private JLabel downScale;
|
private JLabel downScale;
|
||||||
|
private JLabel time;
|
||||||
|
|
||||||
public void setup(){
|
public void setup(){
|
||||||
this.root.setOpaque(false);
|
this.root.setOpaque(false);
|
||||||
|
@ -44,4 +45,8 @@ public class DebugOverlay {
|
||||||
public JPanel getRoot() {
|
public JPanel getRoot() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTime(long l) {
|
||||||
|
this.time.setText(String.format("%.1fs", l * 1e-3));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,11 @@ import shading.Material;
|
||||||
|
|
||||||
public class BasicMesh extends Mesh {
|
public class BasicMesh extends Mesh {
|
||||||
|
|
||||||
private BasicMaterial material;
|
private Material material;
|
||||||
|
|
||||||
private Primitive shape;
|
private Primitive shape;
|
||||||
|
|
||||||
public BasicMesh(BasicMaterial material, Primitive shape) {
|
public BasicMesh(Material material, Primitive shape) {
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.shape = shape;
|
this.shape = shape;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue