diff --git a/.idea/scopes/Raytracing.xml b/.idea/scopes/Raytracing.xml new file mode 100644 index 0000000..614dd5d --- /dev/null +++ b/.idea/scopes/Raytracing.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/xyz/marsavic/gfxlab/graphics3d/Hit.java b/src/xyz/marsavic/gfxlab/graphics3d/Hit.java index c46ab54..ad35754 100644 --- a/src/xyz/marsavic/gfxlab/graphics3d/Hit.java +++ b/src/xyz/marsavic/gfxlab/graphics3d/Hit.java @@ -7,12 +7,18 @@ import xyz.marsavic.gfxlab.Vec3; /** Interaction of a ray with a solid.*/ public interface Hit { - /** The time of the hit */ + /** The time of the hit. */ double t(); - /** The normal at the point of the hit */ + /** The normal at the hit point. */ Vec3 n(); + /** Surface material at the hit point. */ + Material material(); + + /** 2D coordinates in the internal coordinate system of the surface. */ + Vector uv(); + /** The normalized normal at the point of the hit */ default Vec3 n_() { return n().normalized_(); diff --git a/src/xyz/marsavic/gfxlab/graphics3d/Light.java b/src/xyz/marsavic/gfxlab/graphics3d/Light.java new file mode 100644 index 0000000..ee5d95d --- /dev/null +++ b/src/xyz/marsavic/gfxlab/graphics3d/Light.java @@ -0,0 +1,20 @@ +package xyz.marsavic.gfxlab.graphics3d; + +import xyz.marsavic.gfxlab.Color; +import xyz.marsavic.gfxlab.Vec3; + +/** Point light. */ +public record Light( + Vec3 p, + Color c +) { + + public static Light pc(Vec3 p, Color c) { + return new Light(p, c); + } + + public static Light p(Vec3 p) { + return pc(p, Color.WHITE); + } + +} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/Material.java b/src/xyz/marsavic/gfxlab/graphics3d/Material.java new file mode 100644 index 0000000..ed3adb1 --- /dev/null +++ b/src/xyz/marsavic/gfxlab/graphics3d/Material.java @@ -0,0 +1,16 @@ +package xyz.marsavic.gfxlab.graphics3d; + +import xyz.marsavic.gfxlab.Color; + +public record Material( + Color diffuse +) { + public Material diffuse(Color diffuse) { return new Material(diffuse); } + + public static final Material BLACK = new Material(Color.BLACK); + + public static Material matte (Color c) { return BLACK.diffuse(c); } + public static Material matte (double k) { return matte(Color.gray(k)); } + public static Material matte ( ) { return matte(Color.WHITE); } + public static final Material MATTE = matte(); +} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/Scene.java b/src/xyz/marsavic/gfxlab/graphics3d/Scene.java new file mode 100644 index 0000000..154b7e6 --- /dev/null +++ b/src/xyz/marsavic/gfxlab/graphics3d/Scene.java @@ -0,0 +1,44 @@ +package xyz.marsavic.gfxlab.graphics3d; + +import xyz.marsavic.gfxlab.Color; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + + +public interface Scene { + Solid solid(); + + Collection lights(); + + default Color colorBackground() { + return Color.BLACK; + } + + + class Base implements Scene { + + protected Solid solid; + protected final List lights = new ArrayList<>(); + protected final Color colorBackground = Color.BLACK; + + @Override + public Solid solid() { + return solid; + } + + @Override + public Collection lights() { + return lights; + } + + @Override + public Color colorBackground() { + return colorBackground; + } + + } + + +} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracer.java b/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracer.java new file mode 100644 index 0000000..d853a83 --- /dev/null +++ b/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracer.java @@ -0,0 +1,29 @@ +package xyz.marsavic.gfxlab.graphics3d.raytracers; + +import xyz.marsavic.geometry.Vector; +import xyz.marsavic.gfxlab.Color; +import xyz.marsavic.gfxlab.ColorFunctionT; +import xyz.marsavic.gfxlab.Vec3; +import xyz.marsavic.gfxlab.graphics3d.Ray; +import xyz.marsavic.gfxlab.graphics3d.Scene; + + +public abstract class RayTracer implements ColorFunctionT { + + protected final Scene scene; + + + public RayTracer(Scene scene) { + this.scene = scene; + } + + + protected abstract Color sample(Ray r); + + @Override + public Color at(double t, Vector p) { + Ray ray = Ray.pd(Vec3.xyz(0, 0, -2.6), Vec3.zp(1.6, p)); + return sample(ray); + } + +} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracerSimple.java b/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracerSimple.java new file mode 100644 index 0000000..9e65de6 --- /dev/null +++ b/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracerSimple.java @@ -0,0 +1,44 @@ +package xyz.marsavic.gfxlab.graphics3d.raytracers; + +import xyz.marsavic.gfxlab.Color; +import xyz.marsavic.gfxlab.Vec3; +import xyz.marsavic.gfxlab.graphics3d.*; + + +public class RayTracerSimple extends RayTracer { + + public RayTracerSimple(Scene scene) { + super(scene); + } + + @Override + protected Color sample(Ray r) { + Hit hit = scene.solid().firstHit(r); + if (hit == null) { + return scene.colorBackground(); + } + + Vec3 p = r.at(hit.t()); // The hit point + Vec3 n_ = hit.n_(); // Normalized normal to the body surface at the hit point + + Color lightDiffuse = Color.BLACK; // The sum of diffuse contributions from all the lights + + for (Light light : scene.lights()) { + Vec3 l = light.p().sub(p); // Vector from p to the light; + double lLSqr = l.lengthSquared(); // Distance from p to the light squared + double lL = Math.sqrt(lLSqr); // Distance from p to the light + double cosLN = n_.dot(l) / lL; // Cosine of the angle between l and n_ + + if (cosLN > 0) { // If the light is above the surface + Color irradiance = light.c().mul(cosLN / lLSqr); + // The irradiance represents how much light is received by a unit area of the surface. It is + // proportional to the cosine of the incoming angle and inversely proportional to the distance squared + // (inverse-square law). + lightDiffuse = lightDiffuse.add(irradiance); + } + } + + return hit.material().diffuse().mul(lightDiffuse); + } + +} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracingTest.java b/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracingTest.java deleted file mode 100644 index ede21f2..0000000 --- a/src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracingTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package xyz.marsavic.gfxlab.graphics3d.raytracers; - -import xyz.marsavic.geometry.Vector; -import xyz.marsavic.gfxlab.Color; -import xyz.marsavic.gfxlab.ColorFunctionT; -import xyz.marsavic.gfxlab.Vec3; -import xyz.marsavic.gfxlab.graphics3d.Hit; -import xyz.marsavic.gfxlab.graphics3d.Ray; -import xyz.marsavic.gfxlab.graphics3d.solids.Ball; -import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace; - - -public class RayTracingTest implements ColorFunctionT { - - Ball ball = Ball.cr(Vec3.xyz(0, 0, 2), 1); - HalfSpace halfSpace = HalfSpace.pn(Vec3.xyz(0, -1, 0), Vec3.xyz(0, 1, 0)); - - @Override - public Color at(double t, Vector p) { - Ray ray = Ray.pq(Vec3.ZERO, Vec3.zp(1, p)); - - Hit hit1 = ball.firstHit(ray); - Hit hit2 = halfSpace.firstHit(ray); - - double tMin = Math.min(Hit.t(hit1), Hit.t(hit2)); - - return Color.gray(1.0 / (1.0 + tMin)); - } - -} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/scene/SceneTest1.java b/src/xyz/marsavic/gfxlab/graphics3d/scene/SceneTest1.java new file mode 100644 index 0000000..deb6e3c --- /dev/null +++ b/src/xyz/marsavic/gfxlab/graphics3d/scene/SceneTest1.java @@ -0,0 +1,34 @@ +package xyz.marsavic.gfxlab.graphics3d.scene; + +import xyz.marsavic.gfxlab.Color; +import xyz.marsavic.gfxlab.Vec3; +import xyz.marsavic.gfxlab.graphics3d.Light; +import xyz.marsavic.gfxlab.graphics3d.Material; +import xyz.marsavic.gfxlab.graphics3d.Scene; +import xyz.marsavic.gfxlab.graphics3d.solids.Ball; +import xyz.marsavic.gfxlab.graphics3d.solids.Group; +import xyz.marsavic.gfxlab.graphics3d.solids.HalfSpace; + +import java.util.Collections; + + +public class SceneTest1 extends Scene.Base{ + + public SceneTest1() { + solid = Group.of( + Ball.cr(Vec3.xyz(0, 0, 0), 1, + uv -> new Material(Color.hsb(uv.x() * 6, 0.8, uv.y())) + ), + HalfSpace.pn(Vec3.xyz(0, -1, 0), Vec3.xyz(0, 1, 0), + uv -> new Material(Color.hsb(uv.x(), 0.8, 0.8)) + ) + ); + + Collections.addAll(lights, + Light.pc(Vec3.xyz(-1, 1, -1), Color.hsb(0.0, 1.0, 0.6)), + Light.pc(Vec3.xyz( 2, 0, 0), Color.gray(0.6)), + Light.pc(Vec3.xyz( 0, 0, -2), Color.gray(0.1)) + ); + } + +} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/solids/Ball.java b/src/xyz/marsavic/gfxlab/graphics3d/solids/Ball.java index 789588b..408b5ac 100644 --- a/src/xyz/marsavic/gfxlab/graphics3d/solids/Ball.java +++ b/src/xyz/marsavic/gfxlab/graphics3d/solids/Ball.java @@ -1,8 +1,10 @@ package xyz.marsavic.gfxlab.graphics3d.solids; +import xyz.marsavic.functions.interfaces.F1; import xyz.marsavic.geometry.Vector; import xyz.marsavic.gfxlab.Vec3; import xyz.marsavic.gfxlab.graphics3d.Hit; +import xyz.marsavic.gfxlab.graphics3d.Material; import xyz.marsavic.gfxlab.graphics3d.Ray; import xyz.marsavic.gfxlab.graphics3d.Solid; import xyz.marsavic.utils.Numeric; @@ -12,21 +14,22 @@ public class Ball implements Solid { private final Vec3 c; private final double r; + private final F1 mapMaterial; // transient private final double rSqr; - - private Ball(Vec3 c, double r) { + private Ball(Vec3 c, double r, F1 mapMaterial) { this.c = c; this.r = r; rSqr = r * r; + this.mapMaterial = mapMaterial; } - public static Ball cr(Vec3 c, double r) { - return new Ball(c, r); + public static Ball cr(Vec3 c, double r, F1 mapMaterial) { + return new Ball(c, r, mapMaterial); } @@ -69,6 +72,20 @@ public class Ball implements Solid { return ray().at(t()).sub(c()); } + @Override + public Material material() { + return Ball.this.mapMaterial.at(uv()); + } + + @Override + public Vector uv() { + Vec3 n = n(); + return Vector.xy( + Numeric.atan2T(n.z(), n.x()), + -2 * Numeric.asinT(n.y() / r) + 0.5 + ); + } + @Override public Vec3 n_() { return n().div(r); diff --git a/src/xyz/marsavic/gfxlab/graphics3d/solids/Group.java b/src/xyz/marsavic/gfxlab/graphics3d/solids/Group.java new file mode 100644 index 0000000..72361db --- /dev/null +++ b/src/xyz/marsavic/gfxlab/graphics3d/solids/Group.java @@ -0,0 +1,42 @@ +package xyz.marsavic.gfxlab.graphics3d.solids; + +import xyz.marsavic.gfxlab.graphics3d.Hit; +import xyz.marsavic.gfxlab.graphics3d.Ray; +import xyz.marsavic.gfxlab.graphics3d.Solid; + +import java.util.Collection; + +public class Group implements Solid { + + private final Solid[] solids; + + + private Group(Solid... solids) { + this.solids = solids.clone(); + } + + public static Group of(Solid... solids) { + return new Group(solids); + } + + public static Group of(Collection solids) { + return new Group(solids.toArray(Solid[]::new)); + } + + @Override + public Hit firstHit(Ray ray, double afterTime) { + double minT = Double.POSITIVE_INFINITY; + Hit minHit = null; + + for (Solid s : solids) { + Hit hit = s.firstHit(ray, afterTime); + double t = Hit.t(hit); + if (t < minT) { + minT = t; + minHit = hit; + } + } + + return minHit; + } +} diff --git a/src/xyz/marsavic/gfxlab/graphics3d/solids/HalfSpace.java b/src/xyz/marsavic/gfxlab/graphics3d/solids/HalfSpace.java index 1c18214..e5e0d4a 100644 --- a/src/xyz/marsavic/gfxlab/graphics3d/solids/HalfSpace.java +++ b/src/xyz/marsavic/gfxlab/graphics3d/solids/HalfSpace.java @@ -1,10 +1,9 @@ package xyz.marsavic.gfxlab.graphics3d.solids; +import xyz.marsavic.functions.interfaces.F1; +import xyz.marsavic.geometry.Vector; import xyz.marsavic.gfxlab.Vec3; -import xyz.marsavic.gfxlab.graphics3d.GeometryUtils; -import xyz.marsavic.gfxlab.graphics3d.Hit; -import xyz.marsavic.gfxlab.graphics3d.Ray; -import xyz.marsavic.gfxlab.graphics3d.Solid; +import xyz.marsavic.gfxlab.graphics3d.*; public class HalfSpace implements Solid { @@ -12,6 +11,7 @@ public class HalfSpace implements Solid { private final Vec3 p; // A point on the boundary plane private final Vec3 e; // A vector parallel to the boundary plane. private final Vec3 f; // A vector parallel to the boundary plane, not parallel to e. + private final F1 mapMaterial; // transient private final Vec3 n; // A normal vector to the boundary plane @@ -20,10 +20,11 @@ public class HalfSpace implements Solid { - private HalfSpace(Vec3 p, Vec3 e, Vec3 f) { + private HalfSpace(Vec3 p, Vec3 e, Vec3 f, F1 mapMaterial) { this.p = p; this.e = e; this.f = f; + this.mapMaterial = mapMaterial; this.n = e.cross(f); n_ = n.normalized_(); @@ -37,21 +38,21 @@ public class HalfSpace implements Solid { } - public static HalfSpace pef(Vec3 p, Vec3 e, Vec3 f) { - return new HalfSpace(p, e, f); + public static HalfSpace pef(Vec3 p, Vec3 e, Vec3 f, F1 mapMaterial) { + return new HalfSpace(p, e, f, mapMaterial); } - public static HalfSpace pqr(Vec3 p, Vec3 q, Vec3 r) { - return pef(p, q.sub(p), r.sub(p)); + public static HalfSpace pqr(Vec3 p, Vec3 q, Vec3 r, F1 mapMaterial) { + return pef(p, q.sub(p), r.sub(p), mapMaterial); } - public static HalfSpace pn(Vec3 p, Vec3 n) { + public static HalfSpace pn(Vec3 p, Vec3 n, F1 mapMaterial) { double nl = n.length(); Vec3 e = GeometryUtils.normal(n).normalizedTo(nl); Vec3 f = n.cross(e).normalizedTo(nl); - return new HalfSpace(p, e, f); + return new HalfSpace(p, e, f, mapMaterial); } @@ -113,11 +114,30 @@ public class HalfSpace implements Solid { return n; } + @Override + public Material material() { + return HalfSpace.this.mapMaterial.at(uv()); + } + @Override public Vec3 n_() { return n_; } + @Override + public Vector uv() { + Vec3 b = ray().at(t()).sub(p); + + double b_e = b.dot(e) / eLSqr; + double b_f = b.dot(f) / fLSqr; + + return Vector.xy( + (b_e - b_f * f_e) / sinSqr, + (b_f - b_e * e_f) / sinSqr + ); + } } + } + diff --git a/src/xyz/marsavic/gfxlab/gui/UtilsGL.java b/src/xyz/marsavic/gfxlab/gui/UtilsGL.java index 0f886e7..d1fa55d 100644 --- a/src/xyz/marsavic/gfxlab/gui/UtilsGL.java +++ b/src/xyz/marsavic/gfxlab/gui/UtilsGL.java @@ -15,10 +15,9 @@ import xyz.marsavic.gfxlab.Color; import xyz.marsavic.gfxlab.Matrix; import xyz.marsavic.gfxlab.MatrixData; import xyz.marsavic.gfxlab.MatrixInts; -import xyz.marsavic.resources.ResourceManagerMap; import xyz.marsavic.random.RNG; +import xyz.marsavic.resources.ResourceManagerMap; import xyz.marsavic.time.Profiler; -import xyz.marsavic.utils.Utils; import javax.imageio.ImageIO; import java.io.File; @@ -139,13 +138,13 @@ public class UtilsGL { static { int p = ForkJoinPool.getCommonPoolParallelism(); try { - boolean obsRunning = false; - obsRunning |= ProcessHandle.allProcesses().anyMatch(ph -> ph.info().command().orElse("").contains("obs64")); // Windows - obsRunning |= !Utils.runCommand("top -b -n 1 | grep \" obs\"").isEmpty(); // Linux - obsRunning |= true; - if (obsRunning) { - p -= 3; - } +// boolean obsRunning = false; +// obsRunning |= ProcessHandle.allProcesses().anyMatch(ph -> ph.info().command().orElse("").contains("obs64")); // Windows +// obsRunning |= !Utils.runCommand("top -b -n 1 | grep \" obs\"").isEmpty(); // Linux +// obsRunning |= true; +// if (obsRunning) { + p = 1 + p/2; +// } } catch (Exception e) { e.printStackTrace(); } diff --git a/src/xyz/marsavic/gfxlab/playground/GfxLab.java b/src/xyz/marsavic/gfxlab/playground/GfxLab.java index 1cffe32..ec749b4 100644 --- a/src/xyz/marsavic/gfxlab/playground/GfxLab.java +++ b/src/xyz/marsavic/gfxlab/playground/GfxLab.java @@ -4,11 +4,12 @@ import xyz.marsavic.functions.interfaces.A2; import xyz.marsavic.functions.interfaces.F1; import xyz.marsavic.gfxlab.*; import xyz.marsavic.gfxlab.elements.Output; -import xyz.marsavic.gfxlab.graphics3d.raytracers.RayTracingTest; +import xyz.marsavic.gfxlab.graphics3d.raytracers.RayTracerSimple; +import xyz.marsavic.gfxlab.graphics3d.scene.SceneTest1; import xyz.marsavic.gfxlab.gui.UtilsGL; import xyz.marsavic.gfxlab.tonemapping.ColorTransform; import xyz.marsavic.gfxlab.tonemapping.ToneMapping; -import xyz.marsavic.gfxlab.tonemapping.colortransforms.Identity; +import xyz.marsavic.gfxlab.tonemapping.colortransforms.Multiply; import static xyz.marsavic.gfxlab.elements.ElementF.e; import static xyz.marsavic.gfxlab.elements.Output.val; @@ -28,13 +29,15 @@ public class GfxLab { e(Fs::aFillFrameColor, e(Fs::transformedColorFunction, // e(Blobs::new, val(5), val(0.1), val(0.2)), - e(RayTracingTest::new), + e(RayTracerSimple::new, + e(SceneTest1::new) + ), e(TransformationsFromSize.toGeometric, eSize) ) ), e(Fs::toneMapping, e(ColorTransform::asColorTransformFromMatrixColor, - e(Identity::new) + e(Multiply::new, val(1.0)) ) ) ) @@ -70,7 +73,7 @@ class Fs { public static ToneMapping toneMapping(F1> f_ColorTransform_MatrixColor) { return (input, output) -> { ColorTransform f = f_ColorTransform_MatrixColor.at(input); - output.fill(p -> f.at(input.get(p)).code()); + output.fill(p -> f.at(input.get(p)).codeClamp()); }; }