HalfSpace, Group, Material, uv mapping, Light, Scene, diffuse lighting
This commit is contained in:
parent
7f8dbce165
commit
5e56c197f2
3
.idea/scopes/Raytracing.xml
Normal file
3
.idea/scopes/Raytracing.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<component name="DependencyValidationManager">
|
||||
<scope name="Raytracing" pattern="(!lib:*..*&&!test:*..*&&!src:*..*&&!file[GfxLab]:*/&&!ext:*/||src:xyz.marsavic.gfxlab.graphics3d..*||src:xyz.marsavic.gfxlab.playground..*)&&!src:xyz.marsavic.gfxlab.playground.colorfunctions..*" />
|
||||
</component>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -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_();
|
||||
|
|
20
src/xyz/marsavic/gfxlab/graphics3d/Light.java
Normal file
20
src/xyz/marsavic/gfxlab/graphics3d/Light.java
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
16
src/xyz/marsavic/gfxlab/graphics3d/Material.java
Normal file
16
src/xyz/marsavic/gfxlab/graphics3d/Material.java
Normal file
|
@ -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();
|
||||
}
|
44
src/xyz/marsavic/gfxlab/graphics3d/Scene.java
Normal file
44
src/xyz/marsavic/gfxlab/graphics3d/Scene.java
Normal file
|
@ -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<Light> lights();
|
||||
|
||||
default Color colorBackground() {
|
||||
return Color.BLACK;
|
||||
}
|
||||
|
||||
|
||||
class Base implements Scene {
|
||||
|
||||
protected Solid solid;
|
||||
protected final List<Light> lights = new ArrayList<>();
|
||||
protected final Color colorBackground = Color.BLACK;
|
||||
|
||||
@Override
|
||||
public Solid solid() {
|
||||
return solid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Light> lights() {
|
||||
return lights;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color colorBackground() {
|
||||
return colorBackground;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
29
src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracer.java
Normal file
29
src/xyz/marsavic/gfxlab/graphics3d/raytracers/RayTracer.java
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
34
src/xyz/marsavic/gfxlab/graphics3d/scene/SceneTest1.java
Normal file
34
src/xyz/marsavic/gfxlab/graphics3d/scene/SceneTest1.java
Normal file
|
@ -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))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Material, Vector> mapMaterial;
|
||||
|
||||
// transient
|
||||
private final double rSqr;
|
||||
|
||||
|
||||
|
||||
private Ball(Vec3 c, double r) {
|
||||
private Ball(Vec3 c, double r, F1<Material, Vector> 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<Material, Vector> 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);
|
||||
|
|
42
src/xyz/marsavic/gfxlab/graphics3d/solids/Group.java
Normal file
42
src/xyz/marsavic/gfxlab/graphics3d/solids/Group.java
Normal file
|
@ -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<Solid> 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;
|
||||
}
|
||||
}
|
|
@ -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<Material, Vector> 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<Material, Vector> 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<Material, Vector> 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<Material, Vector> 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<Material, Vector> 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<ColorTransform, Matrix<Color>> 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());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue