diff --git a/src/main/java/labs/introtoprogramming/lab5/object/Sphere.java b/src/main/java/labs/introtoprogramming/lab5/object/Sphere.java index 5576e72..8982448 100644 --- a/src/main/java/labs/introtoprogramming/lab5/object/Sphere.java +++ b/src/main/java/labs/introtoprogramming/lab5/object/Sphere.java @@ -20,6 +20,7 @@ public Sphere(Transform transform, double radius) { this.radius = radius; } + @SuppressWarnings("Duplicates") @Override public boolean intersect(Ray ray) { Vector3 originToCenter = transform.position().subtract(ray.getOrigin()); @@ -55,4 +56,8 @@ public Box getBoundary() { Vector3 size = Vector3.ONE.multiply(radius); return new Box(new Transform(), pos.subtract(size), pos.add(size)); } + + public Vector3 getNormal(Vector3 hitPoint) { + return hitPoint.subtract(transform.position()).normalize(); + } } diff --git a/src/main/java/labs/introtoprogramming/lab5/scene/BasicRaytracingRender.java b/src/main/java/labs/introtoprogramming/lab5/scene/BasicRaytracingRender.java index cda699e..df64410 100644 --- a/src/main/java/labs/introtoprogramming/lab5/scene/BasicRaytracingRender.java +++ b/src/main/java/labs/introtoprogramming/lab5/scene/BasicRaytracingRender.java @@ -8,7 +8,11 @@ import java.awt.Color; public class BasicRaytracingRender implements SceneRender { + + private static final boolean DEFAULT_APPLY_SHADING = true; + private Raster raster; + private boolean applyShading = DEFAULT_APPLY_SHADING; public BasicRaytracingRender(Scene scene) { this(scene.getCamera().orElseThrow(NoCameraException::new)); @@ -52,12 +56,40 @@ public void render(Scene scene) { Color getColor(Ray primaryRay, Scene scene) { SceneObject obj = findInteraction(primaryRay, scene); - return obj != null ? obj.getMesh().color() : scene.getBackgroundColor(); + + if (obj == null) { + return scene.getBackgroundColor(); + } + + if (!applyShading) { + return obj.getMesh().color(); + } + + double c = 0; + + for (Light light : scene.getLights()) { + Vector3 hitNormal = obj.getNormal(primaryRay.getPoint()); + double bias = 0.001; + Ray nRay = new Ray(primaryRay.getPoint().add(hitNormal.multiply(bias)), light.getTransform().rotation().multiply(-1)); + boolean visible = findInteraction(nRay, scene, light.getMaxDist(primaryRay.getPoint())) == null; + if (visible) { + c += obj.albedo() / Math.PI * light.illuminate(hitNormal, light.getTransform().position().distance(obj.getTransform().position())); + } + } + + if (c > 1) { + c = 1; + } + + return new Color((int) (obj.getMesh().color().getRed() * c), (int) (obj.getMesh().color().getGreen() * c), (int) (obj.getMesh().color().getBlue() * c)); } SceneObject findInteraction(Ray primaryRay, Scene scene) { + return findInteraction(primaryRay, scene, Double.POSITIVE_INFINITY); + } + + SceneObject findInteraction(Ray primaryRay, Scene scene, double minDistance) { SceneObject intersection = null; - double minDistance = Double.POSITIVE_INFINITY; for (SceneObject obj : scene.getSceneObjects()) { if (obj.intersect(primaryRay)) { double distance = primaryRay.getScale(); @@ -67,6 +99,9 @@ SceneObject findInteraction(Ray primaryRay, Scene scene) { } } } + if (intersection != null) { + primaryRay.setScale(minDistance); + } return intersection; } } diff --git a/src/main/java/labs/introtoprogramming/lab5/scene/BasicScene.java b/src/main/java/labs/introtoprogramming/lab5/scene/BasicScene.java index 8554b07..54d29a7 100644 --- a/src/main/java/labs/introtoprogramming/lab5/scene/BasicScene.java +++ b/src/main/java/labs/introtoprogramming/lab5/scene/BasicScene.java @@ -37,10 +37,10 @@ public List getSceneObjects() { } @Override - public List getLights() { + public List getLights() { return entities.stream() - .filter(obj -> obj instanceof PointLight) - .map(obj -> (PointLight) obj) + .filter(obj -> obj instanceof Light) + .map(obj -> (Light) obj) .collect(Collectors.toList()); } diff --git a/src/main/java/labs/introtoprogramming/lab5/scene/DistantLight.java b/src/main/java/labs/introtoprogramming/lab5/scene/DistantLight.java new file mode 100644 index 0000000..734f8c5 --- /dev/null +++ b/src/main/java/labs/introtoprogramming/lab5/scene/DistantLight.java @@ -0,0 +1,22 @@ +package labs.introtoprogramming.lab5.scene; + +import java.awt.Color; +import labs.introtoprogramming.lab5.geometry.Vector3; + +public class DistantLight extends Light { + + Vector3 direction; + + public DistantLight(Transform transform, double intensity) { + super(transform, intensity); + } + + @Override + public double illuminate(Vector3 normal, double distance) { + return Math.max(intensity() * normal.dotProduct(getTransform().rotation().multiply(-1)), 0); + } + + public double getMaxDist(Vector3 point) { + return Double.POSITIVE_INFINITY; + } +} diff --git a/src/main/java/labs/introtoprogramming/lab5/scene/Light.java b/src/main/java/labs/introtoprogramming/lab5/scene/Light.java new file mode 100644 index 0000000..bcd0307 --- /dev/null +++ b/src/main/java/labs/introtoprogramming/lab5/scene/Light.java @@ -0,0 +1,21 @@ +package labs.introtoprogramming.lab5.scene; + +import labs.introtoprogramming.lab5.geometry.Vector3; + +public abstract class Light extends SceneObject { + + private double intensity; + + public Light(Transform transform, double intensity) { + super(transform); + this.intensity = intensity; + } + + public double intensity() { + return intensity; + } + + public abstract double illuminate(Vector3 normal, double distance); + + public abstract double getMaxDist(Vector3 point); +} diff --git a/src/main/java/labs/introtoprogramming/lab5/scene/PointLight.java b/src/main/java/labs/introtoprogramming/lab5/scene/PointLight.java index 5c1d06e..5c0905d 100644 --- a/src/main/java/labs/introtoprogramming/lab5/scene/PointLight.java +++ b/src/main/java/labs/introtoprogramming/lab5/scene/PointLight.java @@ -1,4 +1,19 @@ package labs.introtoprogramming.lab5.scene; -public abstract class PointLight extends SceneObject { +import labs.introtoprogramming.lab5.geometry.Vector3; + +public class PointLight extends Light { + public PointLight(Transform transform, double intensity) { + super(transform, intensity); + } + + @Override + public double illuminate(Vector3 normal, double distance) { + return Math.max((intensity() * normal.dotProduct(getTransform().rotation().multiply(-1))) / (4 * Math.PI * Math.pow(distance, 2)), 0); + } + + @Override + public double getMaxDist(Vector3 point) { + return point.distance(transform.position()); + } } diff --git a/src/main/java/labs/introtoprogramming/lab5/scene/Scene.java b/src/main/java/labs/introtoprogramming/lab5/scene/Scene.java index dfc6ea2..f50c639 100644 --- a/src/main/java/labs/introtoprogramming/lab5/scene/Scene.java +++ b/src/main/java/labs/introtoprogramming/lab5/scene/Scene.java @@ -13,7 +13,7 @@ public abstract class Scene { public abstract List getSceneObjects(); - public abstract List getLights(); + public abstract List getLights(); public abstract List getControllers(); diff --git a/src/main/java/labs/introtoprogramming/lab5/scene/SceneObject.java b/src/main/java/labs/introtoprogramming/lab5/scene/SceneObject.java index d2b3b78..2a61b97 100644 --- a/src/main/java/labs/introtoprogramming/lab5/scene/SceneObject.java +++ b/src/main/java/labs/introtoprogramming/lab5/scene/SceneObject.java @@ -1,12 +1,17 @@ package labs.introtoprogramming.lab5.scene; +import java.awt.Color; import labs.introtoprogramming.lab5.geometry.Ray; +import labs.introtoprogramming.lab5.geometry.Vector3; import labs.introtoprogramming.lab5.object.Box; public class SceneObject { + private static double DEFAULT_ALBEDO = 0.18; + protected Transform transform; protected Mesh mesh; + protected double albedo = DEFAULT_ALBEDO; /** * Base class of scene objects. @@ -39,7 +44,13 @@ public boolean intersect(Ray ray) { return false; } + public Vector3 getNormal(Vector3 hitPoint) { return Vector3.ZERO; } + public Box getBoundary() { return null; } + + public double albedo() { + return albedo; + } } diff --git a/src/main/java/labs/introtoprogramming/lab5/scenes/DemoScene.java b/src/main/java/labs/introtoprogramming/lab5/scenes/DemoScene.java index 6b65c44..3af16cd 100644 --- a/src/main/java/labs/introtoprogramming/lab5/scenes/DemoScene.java +++ b/src/main/java/labs/introtoprogramming/lab5/scenes/DemoScene.java @@ -1,18 +1,26 @@ package labs.introtoprogramming.lab5.scenes; +import java.awt.Color; import labs.introtoprogramming.lab5.geometry.Vector3; import labs.introtoprogramming.lab5.object.Box; import labs.introtoprogramming.lab5.object.Sphere; import labs.introtoprogramming.lab5.scene.BasicScene; +import labs.introtoprogramming.lab5.scene.DistantLight; +import labs.introtoprogramming.lab5.scene.PointLight; import labs.introtoprogramming.lab5.scene.Transform; public class DemoScene extends BasicScene { public DemoScene() { + Box other = new Box(new Transform(new Vector3(0, -6, 0)), 10); + Sphere sphere = new Sphere(new Transform(Vector3.FORWARD.multiply(-3)), 1); + sphere.getMesh().setColor(Color.RED); addSceneObjects( - new Sphere(new Transform(Vector3.FORWARD.multiply(-3)), 1), - new Box(new Transform(Vector3.FORWARD.multiply(-10)), 1), - new Box(new Transform(Vector3.RIGHT.multiply(5)), 1) + sphere, + new Box(new Transform(new Vector3(2, 2, -3)), 1), + other, + new DistantLight(new Transform(Vector3.ZERO, new Vector3(0, -1, 0)), 10) + //new PointLight(new Transform(new Vector3(0, 0, -1), new Vector3(0, -1, 0)), 1000) ); } }