added support for refraction in raytracer
This commit is contained in:
parent
bb03af40e9
commit
4d3c45c111
|
@ -0,0 +1,3 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile name="Maven default annotation processors profile" enabled="true">
|
||||||
|
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||||
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<module name="JPath" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Central Repository" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_19" default="true" project-jdk-name="openjdk-19" project-jdk-type="JavaSDK" />
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -100,6 +100,16 @@ public class Vector {
|
||||||
return this.sub(normal.scale(this.dot(normal) * 2.0));
|
return this.sub(normal.scale(this.dot(normal) * 2.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector refract(Vector normal, Vector incoming, double ior) {
|
||||||
|
double len2 = normal.dot(incoming);
|
||||||
|
double k = 1.0 - ior * ior * (1.0 - len2 * len2);
|
||||||
|
|
||||||
|
if (k < 0)
|
||||||
|
return Vector.origin();
|
||||||
|
|
||||||
|
return incoming.scale(ior).sub(normal.scale(ior * len2 + Math.sqrt(k)));
|
||||||
|
}
|
||||||
|
|
||||||
public Vector lerp(Vector other, double k) {
|
public Vector lerp(Vector other, double k) {
|
||||||
return this.scale(k).add(other.scale(1.0 - k));
|
return this.scale(k).add(other.scale(1.0 - k));
|
||||||
}
|
}
|
||||||
|
@ -108,6 +118,7 @@ public class Vector {
|
||||||
return this.sub(vector).length();
|
return this.sub(vector).length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum SwizzleMask {
|
public enum SwizzleMask {
|
||||||
XYZ,
|
XYZ,
|
||||||
YXZ,
|
YXZ,
|
||||||
|
|
|
@ -9,7 +9,7 @@ public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
Resolution resolution = new Resolution(800, 800, 2);
|
Resolution resolution = new Resolution(800, 800, 3);
|
||||||
|
|
||||||
PlayerController controller = new PlayerController();
|
PlayerController controller = new PlayerController();
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,10 @@ import optics.light.Point;
|
||||||
import raytracing.BasicMaterial;
|
import raytracing.BasicMaterial;
|
||||||
import renderer.mesh.BasicMesh;
|
import renderer.mesh.BasicMesh;
|
||||||
import renderer.mesh.Mesh;
|
import renderer.mesh.Mesh;
|
||||||
|
import renderer.primitive.Disk;
|
||||||
import renderer.primitive.Plane;
|
import renderer.primitive.Plane;
|
||||||
import renderer.primitive.Sphere;
|
import renderer.primitive.Sphere;
|
||||||
|
import shading.Material;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -30,14 +32,27 @@ public class Scene {
|
||||||
public static Scene generateExampleScene() {
|
public static Scene generateExampleScene() {
|
||||||
Scene scene = new Scene(new LinearList());
|
Scene scene = new Scene(new LinearList());
|
||||||
|
|
||||||
scene.addMesh(new BasicMesh(new BasicMaterial(new Color(0.8, 0.4, 0.2), 1.0, new Color(1,0.4,0.1)), new Sphere(new Vector(2,0,0), 1.0)));
|
BasicMaterial mirror = new BasicMaterial(Color.BLACK, 1.0, Color.WHITE, true);
|
||||||
scene.addMesh(new BasicMesh(new BasicMaterial(new Color(0.8, 0.4, 0.2), 0.0, new Color(1,0.4,0.1)), new Sphere(Vector.origin(), 1.0)));
|
BasicMaterial white = new BasicMaterial(new Color(0.8, 0.8, 0.8), 0.1, new Color(0.8, 0.8, 0.8), false);
|
||||||
scene.addMesh(new BasicMesh(new BasicMaterial(new Color(1, 1, 1), 0.2, new Color(1.0)), new Plane(new Vector(1, 0, 0), 1.0)));
|
BasicMaterial red = new BasicMaterial(new Color(0.8, 0.0, 0.0), 0.1, new Color(0.8, 0.8, 0.8), false);
|
||||||
scene.addMesh(new BasicMesh(new BasicMaterial(new Color(1, 1, 1), 0.0, new Color(1.0)), new Plane(new Vector(0, 1, 0), 1.0)));
|
BasicMaterial green = new BasicMaterial(new Color(0.0, 0.8, 0.0), 0.1, new Color(0.8, 0.8, 0.8), false);
|
||||||
|
BasicMaterial blue = new BasicMaterial(new Color(0.0, 0.0, 0.8), 0.1, new Color(0.8, 0.8, 0.8), false);
|
||||||
|
|
||||||
scene.addLight(new Point(new Color(0.9, 0, 0), new Vector(8,3,-2)));
|
scene.addMesh(new BasicMesh(mirror, new Sphere(new Vector(0,1.5,0), 1.0)));
|
||||||
scene.addLight(new Point(new Color(0,0.8,0), new Vector(4,3,2)));
|
scene.addMesh(new BasicMesh(white, new Sphere(new Vector(3,2,0), 0.25)));
|
||||||
//scene.addLight(new Directional(new Color(1), new Vector(1, 1, 0).normalize()));
|
scene.addMesh(new BasicMesh(red, new Sphere(new Vector(2.5,2,-3), 0.33)));
|
||||||
|
|
||||||
|
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.addLight(new Point(Color.WHITE.scale(0.5), new Vector(0, 4, 0)));
|
||||||
|
scene.addLight(new Point(Color.WHITE.scale(0.5), new Vector(1, 3, 1)));
|
||||||
|
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import basics.math.algebra.Vector;
|
||||||
public class Color {
|
public class Color {
|
||||||
|
|
||||||
public static final Color BLACK = new Color(0,0,0);
|
public static final Color BLACK = new Color(0,0,0);
|
||||||
|
public static final Color WHITE = Color.diagonal(1.0);
|
||||||
|
|
||||||
public double r;
|
public double r;
|
||||||
public double g;
|
public double g;
|
||||||
|
|
|
@ -9,10 +9,13 @@ public class BasicMaterial implements Material {
|
||||||
private double glossieness;
|
private double glossieness;
|
||||||
private Color reflectionTint;
|
private Color reflectionTint;
|
||||||
|
|
||||||
public BasicMaterial(Color diffuse, double glossieness, Color reflectionTint) {
|
private boolean refracts;
|
||||||
|
|
||||||
|
public BasicMaterial(Color diffuse, double glossieness, Color reflectionTint, boolean refracts) {
|
||||||
this.diffuse = diffuse;
|
this.diffuse = diffuse;
|
||||||
this.glossieness = glossieness;
|
this.glossieness = glossieness;
|
||||||
this.reflectionTint = reflectionTint;
|
this.reflectionTint = reflectionTint;
|
||||||
|
this.refracts = refracts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color getDiffuse() {
|
public Color getDiffuse() {
|
||||||
|
@ -26,4 +29,8 @@ public class BasicMaterial implements Material {
|
||||||
public Color getReflectionTint() {
|
public Color getReflectionTint() {
|
||||||
return reflectionTint;
|
return reflectionTint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean refracts() {
|
||||||
|
return this.refracts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class Raytracer implements Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color traceDirect(Ray directRay, int depth) {
|
private Color traceDirect(Ray directRay, int depth) {
|
||||||
if (depth == 4) {
|
if (depth == 8) {
|
||||||
return Color.BLACK;
|
return Color.BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,13 +42,17 @@ public class Raytracer implements Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector intersection = directRay.travel(result.get().getDistance());
|
Vector intersection = directRay.travel(result.get().getDistance());
|
||||||
Vector normal = result.get().getMesh().normalAt(intersection);
|
Vector normal = result.get().getMesh().normalAt(intersection, directRay.getDirection());
|
||||||
|
|
||||||
BasicMaterial material = (BasicMaterial) result.get().getMesh().getMaterial();
|
BasicMaterial material = (BasicMaterial) result.get().getMesh().getMaterial();
|
||||||
|
|
||||||
// indirect light sum
|
// indirect light sum
|
||||||
Color incoming = new Color(0,0,0);
|
Color incoming = new Color(0,0,0);
|
||||||
|
|
||||||
|
if (material.refracts()) {
|
||||||
|
return refractedLight(directRay, material, normal, intersection, depth);
|
||||||
|
}
|
||||||
|
|
||||||
Color reflectedLight = reflectedLight(directRay, material, normal, intersection, depth);
|
Color reflectedLight = reflectedLight(directRay, material, normal, intersection, depth);
|
||||||
|
|
||||||
for (LightSource light : scene.getLights()) {
|
for (LightSource light : scene.getLights()) {
|
||||||
|
@ -56,20 +60,26 @@ public class Raytracer implements Renderer {
|
||||||
|
|
||||||
Color directLight = material.getDiffuse().scale(directInfluence).mul(light.getColor());
|
Color directLight = material.getDiffuse().scale(directInfluence).mul(light.getColor());
|
||||||
|
|
||||||
incoming = incoming.add(directLight).add(reflectedLight);
|
incoming = incoming.add(directLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
return incoming.add(reflectedLight);
|
return reflectedLight.lerp(incoming, material.getGlossieness());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color reflectedLight(Ray incomingRay, BasicMaterial material, Vector normal, Vector point, int depth) {
|
private Color reflectedLight(Ray incomingRay, BasicMaterial material, Vector normal, Vector point, int depth) {
|
||||||
Vector reflected = incomingRay.getDirection().reflect(normal);
|
Vector reflected = incomingRay.getDirection().reflect(normal);
|
||||||
|
|
||||||
Color reflectedLight = traceDirect(new Ray(point, reflected, 1e-3, 1e3), depth + 1);
|
Color reflectedLight = traceDirect(new Ray(point, reflected, 1e-3, incomingRay.getFar()), depth + 1);
|
||||||
|
|
||||||
double highlightFactor = reflected.dot(normal);
|
return reflectedLight.mul(material.getReflectionTint());
|
||||||
|
}
|
||||||
|
|
||||||
return reflectedLight.add(new Color(highlightFactor)).mul(material.getReflectionTint());
|
private Color refractedLight(Ray incomingRay, BasicMaterial material, Vector normal, Vector point, int depth) {
|
||||||
|
Vector refracted = incomingRay.getDirection().refract(normal, incomingRay.getDirection(), 1.45);
|
||||||
|
|
||||||
|
Color refractedLight = 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) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class Display {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.window.setContentPane(target);
|
this.window.setContentPane(target);
|
||||||
this.window.setLocationRelativeTo(null);
|
this.window.setLocationByPlatform(true);
|
||||||
this.window.setVisible(true);
|
this.window.setVisible(true);
|
||||||
|
|
||||||
try (Scheduler scheduler = Scheduler.getInstance()) {
|
try (Scheduler scheduler = Scheduler.getInstance()) {
|
||||||
|
|
|
@ -48,19 +48,12 @@ public class ContributionBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private double tonemap(double x) {
|
|
||||||
final double g = 5;
|
|
||||||
final double k = 4;
|
|
||||||
|
|
||||||
return x < g ? 1 - 1 / Math.pow(g, k) * Math.pow(x - g, k) : 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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]);
|
||||||
|
|
||||||
int red = (int) (tonemap(linearRGB.r) * 255.0);
|
int red = (int) (linearRGB.r* 255.0);
|
||||||
int green = (int) (tonemap(linearRGB.g) * 255.0);
|
int green = (int) (linearRGB.g * 255.0);
|
||||||
int blue = (int) (tonemap(linearRGB.b) * 255.0);
|
int blue = (int) (linearRGB.b * 255.0);
|
||||||
|
|
||||||
red = Math.max(Math.min(red, 255), 0);
|
red = Math.max(Math.min(red, 255), 0);
|
||||||
green = Math.max(Math.min(green, 255), 0);
|
green = Math.max(Math.min(green, 255), 0);
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class RenderTarget extends JPanel {
|
||||||
private ContributionBuffer buffer;
|
private ContributionBuffer buffer;
|
||||||
private DebugOverlay overlay;
|
private DebugOverlay overlay;
|
||||||
|
|
||||||
private boolean visualizeThreads = true;
|
private boolean visualizeThreads = false;
|
||||||
|
|
||||||
public RenderTarget(Resolution resolution) {
|
public RenderTarget(Resolution resolution) {
|
||||||
super();
|
super();
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class BasicMesh extends Mesh {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vector normalAt(Vector surfacePoint) {
|
public Vector normalAt(Vector surfacePoint, Vector incoming) {
|
||||||
return shape.normalAt(surfacePoint);
|
return shape.normalAt(surfacePoint, incoming);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ public abstract class Mesh {
|
||||||
|
|
||||||
public abstract double intersect(Ray ray);
|
public abstract double intersect(Ray ray);
|
||||||
|
|
||||||
public abstract Vector normalAt(Vector surfacePoint);
|
public abstract Vector normalAt(Vector surfacePoint, Vector incoming);
|
||||||
|
|
||||||
public abstract Material getMaterial();
|
public abstract Material getMaterial();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package renderer.primitive;
|
||||||
|
|
||||||
|
import basics.math.algebra.Ray;
|
||||||
|
import basics.math.algebra.Vector;
|
||||||
|
|
||||||
|
public class Disk implements Primitive {
|
||||||
|
|
||||||
|
private double offset;
|
||||||
|
private double radius;
|
||||||
|
private Vector normal;
|
||||||
|
|
||||||
|
public Disk(Vector normal, double offset, double radius) {
|
||||||
|
this.offset = offset;
|
||||||
|
this.radius = radius;
|
||||||
|
this.normal = normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double intersect(Ray ray) {
|
||||||
|
double distance = -(ray.getOrigin().dot(normal) + offset) / ray.getDirection().dot(normal);
|
||||||
|
Vector intersection = ray.travel(distance);
|
||||||
|
|
||||||
|
if (intersection.dot(intersection) > this.radius * this.radius) {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector normalAt(Vector surfacePoint, Vector incoming) {
|
||||||
|
return normal.scale(-Math.signum(normal.dot(incoming)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ public class Plane implements Primitive {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vector normalAt(Vector surfacePoint) {
|
public Vector normalAt(Vector surfacePoint, Vector incoming) {
|
||||||
return normal;
|
return normal.scale(-Math.signum(normal.dot(incoming)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@ public interface Primitive {
|
||||||
|
|
||||||
double intersect(Ray ray);
|
double intersect(Ray ray);
|
||||||
|
|
||||||
Vector normalAt(Vector surfacePoint);
|
Vector normalAt(Vector surfacePoint, Vector incoming);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,18 +17,23 @@ public class Sphere implements Primitive {
|
||||||
public double intersect(Ray ray) {
|
public double intersect(Ray ray) {
|
||||||
Vector oc = ray.getOrigin().sub(this.center);
|
Vector oc = ray.getOrigin().sub(this.center);
|
||||||
|
|
||||||
double b = ray.getDirection().dot(oc);
|
double b = ray.getDirection().scale(2).dot(oc);
|
||||||
double c = oc.dot(oc) - this.radius * this.radius;
|
double c = oc.dot(oc) - this.radius * this.radius;
|
||||||
double h = b * b - c;
|
double h = b * b - 4.0 * c;
|
||||||
|
|
||||||
if (h < 0.0)
|
if (h < 0.0)
|
||||||
return -1.0;
|
return -1.0;
|
||||||
|
|
||||||
return -b - h;
|
h = Math.sqrt(h);
|
||||||
|
|
||||||
|
double u = -b - h;
|
||||||
|
double v = -b + h;
|
||||||
|
|
||||||
|
return (u > ray.getNear() ? u : u > ray.getNear() ? v : 0) * 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vector normalAt(Vector surfacePoint) {
|
public Vector normalAt(Vector surfacePoint, Vector incoming) {
|
||||||
return surfacePoint.sub(center).normalize();
|
return surfacePoint.sub(center).normalize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue