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));
|
||||
}
|
||||
|
||||
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) {
|
||||
return this.scale(k).add(other.scale(1.0 - k));
|
||||
}
|
||||
|
@ -108,6 +118,7 @@ public class Vector {
|
|||
return this.sub(vector).length();
|
||||
}
|
||||
|
||||
|
||||
public enum SwizzleMask {
|
||||
XYZ,
|
||||
YXZ,
|
||||
|
|
|
@ -9,7 +9,7 @@ public class Main {
|
|||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Resolution resolution = new Resolution(800, 800, 2);
|
||||
Resolution resolution = new Resolution(800, 800, 3);
|
||||
|
||||
PlayerController controller = new PlayerController();
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@ import optics.light.Point;
|
|||
import raytracing.BasicMaterial;
|
||||
import renderer.mesh.BasicMesh;
|
||||
import renderer.mesh.Mesh;
|
||||
import renderer.primitive.Disk;
|
||||
import renderer.primitive.Plane;
|
||||
import renderer.primitive.Sphere;
|
||||
import shading.Material;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -30,14 +32,27 @@ public class Scene {
|
|||
public static Scene generateExampleScene() {
|
||||
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)));
|
||||
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)));
|
||||
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)));
|
||||
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 mirror = new BasicMaterial(Color.BLACK, 1.0, Color.WHITE, true);
|
||||
BasicMaterial white = new BasicMaterial(new Color(0.8, 0.8, 0.8), 0.1, new Color(0.8, 0.8, 0.8), false);
|
||||
BasicMaterial red = new BasicMaterial(new Color(0.8, 0.0, 0.0), 0.1, new Color(0.8, 0.8, 0.8), false);
|
||||
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.addLight(new Point(new Color(0,0.8,0), new Vector(4,3,2)));
|
||||
//scene.addLight(new Directional(new Color(1), new Vector(1, 1, 0).normalize()));
|
||||
scene.addMesh(new BasicMesh(mirror, new Sphere(new Vector(0,1.5,0), 1.0)));
|
||||
scene.addMesh(new BasicMesh(white, new Sphere(new Vector(3,2,0), 0.25)));
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import basics.math.algebra.Vector;
|
|||
public class Color {
|
||||
|
||||
public static final Color BLACK = new Color(0,0,0);
|
||||
public static final Color WHITE = Color.diagonal(1.0);
|
||||
|
||||
public double r;
|
||||
public double g;
|
||||
|
|
|
@ -9,10 +9,13 @@ public class BasicMaterial implements Material {
|
|||
private double glossieness;
|
||||
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.glossieness = glossieness;
|
||||
this.reflectionTint = reflectionTint;
|
||||
this.refracts = refracts;
|
||||
}
|
||||
|
||||
public Color getDiffuse() {
|
||||
|
@ -26,4 +29,8 @@ public class BasicMaterial implements Material {
|
|||
public Color getReflectionTint() {
|
||||
return reflectionTint;
|
||||
}
|
||||
|
||||
public boolean refracts() {
|
||||
return this.refracts;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public class Raytracer implements Renderer {
|
|||
}
|
||||
|
||||
private Color traceDirect(Ray directRay, int depth) {
|
||||
if (depth == 4) {
|
||||
if (depth == 8) {
|
||||
return Color.BLACK;
|
||||
}
|
||||
|
||||
|
@ -42,13 +42,17 @@ public class Raytracer implements Renderer {
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
// indirect light sum
|
||||
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);
|
||||
|
||||
for (LightSource light : scene.getLights()) {
|
||||
|
@ -56,20 +60,26 @@ public class Raytracer implements Renderer {
|
|||
|
||||
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) {
|
||||
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) {
|
||||
|
|
|
@ -80,7 +80,7 @@ public class Display {
|
|||
};
|
||||
|
||||
this.window.setContentPane(target);
|
||||
this.window.setLocationRelativeTo(null);
|
||||
this.window.setLocationByPlatform(true);
|
||||
this.window.setVisible(true);
|
||||
|
||||
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) {
|
||||
Color linearRGB = buffer[x][y].scale(1.0 / samples[x][y]);
|
||||
|
||||
int red = (int) (tonemap(linearRGB.r) * 255.0);
|
||||
int green = (int) (tonemap(linearRGB.g) * 255.0);
|
||||
int blue = (int) (tonemap(linearRGB.b) * 255.0);
|
||||
int red = (int) (linearRGB.r* 255.0);
|
||||
int green = (int) (linearRGB.g * 255.0);
|
||||
int blue = (int) (linearRGB.b * 255.0);
|
||||
|
||||
red = Math.max(Math.min(red, 255), 0);
|
||||
green = Math.max(Math.min(green, 255), 0);
|
||||
|
|
|
@ -17,7 +17,7 @@ public class RenderTarget extends JPanel {
|
|||
private ContributionBuffer buffer;
|
||||
private DebugOverlay overlay;
|
||||
|
||||
private boolean visualizeThreads = true;
|
||||
private boolean visualizeThreads = false;
|
||||
|
||||
public RenderTarget(Resolution resolution) {
|
||||
super();
|
||||
|
|
|
@ -28,7 +28,7 @@ public class BasicMesh extends Mesh {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Vector normalAt(Vector surfacePoint) {
|
||||
return shape.normalAt(surfacePoint);
|
||||
public Vector normalAt(Vector surfacePoint, Vector incoming) {
|
||||
return shape.normalAt(surfacePoint, incoming);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ public abstract class Mesh {
|
|||
|
||||
public abstract double intersect(Ray ray);
|
||||
|
||||
public abstract Vector normalAt(Vector surfacePoint);
|
||||
public abstract Vector normalAt(Vector surfacePoint, Vector incoming);
|
||||
|
||||
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
|
||||
public Vector normalAt(Vector surfacePoint) {
|
||||
return normal;
|
||||
public Vector normalAt(Vector surfacePoint, Vector incoming) {
|
||||
return normal.scale(-Math.signum(normal.dot(incoming)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,5 @@ public interface Primitive {
|
|||
|
||||
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) {
|
||||
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 h = b * b - c;
|
||||
double h = b * b - 4.0 * c;
|
||||
|
||||
if (h < 0.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
|
||||
public Vector normalAt(Vector surfacePoint) {
|
||||
public Vector normalAt(Vector surfacePoint, Vector incoming) {
|
||||
return surfacePoint.sub(center).normalize();
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue