added diffuse pathtracing

This commit is contained in:
Sven Vogel 2023-04-10 14:29:30 +02:00
parent 3d3bc7bd95
commit b882192fe7
17 changed files with 457 additions and 27 deletions

9
.idea/jpath.iml Normal file
View File

@ -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>

124
.idea/uiDesigner.xml Normal file
View File

@ -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>

View File

@ -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);
}
}

View File

@ -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));
} }

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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);
} }
} }

View File

@ -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));
} }

View File

@ -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;

View File

@ -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;
}
} }

View File

@ -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">

View File

@ -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));
}
} }

View File

@ -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;
} }