allow multiple sampling targets in a scene

main
jbb01 6 months ago
parent 6e35453932
commit ed9e50b8f2

@ -51,14 +51,13 @@ public class Examples {
public static @NotNull Example getSimpleScene(int height) { public static @NotNull Example getSimpleScene(int height) {
if (height <= 0) height = 675; if (height <= 0) height = 675;
return new Example( return new Example(
new Scene( new Scene(getSkyBox(), List.of(
getSkyBox(),
new Sphere(new Vec3(0, -100.5, -1.0), 100.0, new LambertianMaterial(new Color(0.8, 0.8, 0.0))), new Sphere(new Vec3(0, -100.5, -1.0), 100.0, new LambertianMaterial(new Color(0.8, 0.8, 0.0))),
new Sphere(new Vec3(0, 0, -1.2), 0.5, new LambertianMaterial(new Color(0.1, 0.2, 0.5))), new Sphere(new Vec3(0, 0, -1.2), 0.5, new LambertianMaterial(new Color(0.1, 0.2, 0.5))),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.5, new DielectricMaterial(1.5)), new Sphere(new Vec3(-1.0, 0, -1.2), 0.5, new DielectricMaterial(1.5)),
new Sphere(new Vec3(-1.0, 0, -1.2), 0.4, new DielectricMaterial(1 / 1.5)), new Sphere(new Vec3(-1.0, 0, -1.2), 0.4, new DielectricMaterial(1 / 1.5)),
new Sphere(new Vec3(1.0, 0, -1.2), 0.5, new MetallicMaterial(new Color(0.8, 0.6, 0.2), 0.0)) new Sphere(new Vec3(1.0, 0, -1.2), 0.5, new MetallicMaterial(new Color(0.8, 0.6, 0.2), 0.0))
), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height * 16 / 9, height) .withImage(height * 16 / 9, height)
.build() .build()
@ -119,14 +118,13 @@ public class Examples {
public static @NotNull Example getSquares(int height) { public static @NotNull Example getSquares(int height) {
if (height <= 0) height = 600; if (height <= 0) height = 600;
return new Example( return new Example(
new Scene( new Scene(getSkyBox(), List.of(
getSkyBox(),
new Parallelogram(new Vec3(-3, -2, 5), new Vec3(0, 0, -4), new Vec3(0, 4, 0), new LambertianMaterial(new Color(1.0, 0.2, 0.2))), new Parallelogram(new Vec3(-3, -2, 5), new Vec3(0, 0, -4), new Vec3(0, 4, 0), new LambertianMaterial(new Color(1.0, 0.2, 0.2))),
new Parallelogram(new Vec3(-2, -2, 0), new Vec3(4, 0, 0), new Vec3(0, 4, 0), new LambertianMaterial(new Color(0.2, 1.0, 0.2))), new Parallelogram(new Vec3(-2, -2, 0), new Vec3(4, 0, 0), new Vec3(0, 4, 0), new LambertianMaterial(new Color(0.2, 1.0, 0.2))),
new Parallelogram(new Vec3(3, -2, 1), new Vec3(0, 0, 4), new Vec3(0, 4, 0), new LambertianMaterial(new Color(0.2, 0.2, 1.0))), new Parallelogram(new Vec3(3, -2, 1), new Vec3(0, 0, 4), new Vec3(0, 4, 0), new LambertianMaterial(new Color(0.2, 0.2, 1.0))),
new Parallelogram(new Vec3(-2, 3, 1), new Vec3(4, 0, 0), new Vec3(0, 0, 4), new LambertianMaterial(new Color(1.0, 0.5, 0.0))), new Parallelogram(new Vec3(-2, 3, 1), new Vec3(4, 0, 0), new Vec3(0, 0, 4), new LambertianMaterial(new Color(1.0, 0.5, 0.0))),
new Parallelogram(new Vec3(-2, -3, 5), new Vec3(4, 0, 0), new Vec3(0, 0, -4), new LambertianMaterial(new Color(0.2, 0.8, 0.8))) new Parallelogram(new Vec3(-2, -3, 5), new Vec3(4, 0, 0), new Vec3(0, 0, -4), new LambertianMaterial(new Color(0.2, 0.8, 0.8)))
), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height, height) .withImage(height, height)
.withFieldOfView(Math.toRadians(80)) .withFieldOfView(Math.toRadians(80))
@ -139,12 +137,12 @@ public class Examples {
public static @NotNull Example getLight(int height) { public static @NotNull Example getLight(int height) {
if (height <= 0) height = 225; if (height <= 0) height = 225;
return new Example( return new Example(
new Scene( new Scene(List.of(
new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new Color(0.2, 0.2, 0.2))), new Sphere(new Vec3(0, -1000, 0), 1000, new LambertianMaterial(new Color(0.2, 0.2, 0.2))),
new Sphere(new Vec3(0, 2, 0), 2, new LambertianMaterial(new Color(0.2, 0.2, 0.2))), new Sphere(new Vec3(0, 2, 0), 2, new LambertianMaterial(new Color(0.2, 0.2, 0.2))),
new Parallelogram(new Vec3(3, 1, -2), new Vec3(2, 0, 0), new Vec3(0, 2, 0), new DiffuseLight(new Color(4.0, 4.0, 4.0))), new Parallelogram(new Vec3(3, 1, -2), new Vec3(2, 0, 0), new Vec3(0, 2, 0), new DiffuseLight(new Color(4.0, 4.0, 4.0))),
new Sphere(new Vec3(0, 7, 0), 2, new DiffuseLight(new Color(4.0, 4.0, 4.0))) new Sphere(new Vec3(0, 7, 0), 2, new DiffuseLight(new Color(4.0, 4.0, 4.0)))
), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height * 16 / 9, height) .withImage(height * 16 / 9, height)
.withFieldOfView(Math.toRadians(20)) .withFieldOfView(Math.toRadians(20))
@ -163,7 +161,7 @@ public class Examples {
var light = new DiffuseLight(new Color(15.0, 15.0, 15.0)); var light = new DiffuseLight(new Color(15.0, 15.0, 15.0));
return new Example( return new Example(
new Scene( new Scene(List.of(
new Box( new Box(
new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)), new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)),
white, white, red, green, white, null white, white, red, green, white, null
@ -171,7 +169,7 @@ public class Examples {
new Parallelogram(new Vec3(343, 554, 332), new Vec3(-130, 0, 0), new Vec3(0, 0, -105), light), new Parallelogram(new Vec3(343, 554, 332), new Vec3(-130, 0, 0), new Vec3(0, 0, -105), light),
new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)), new Box(new Vec3(0, 0, 0), new Vec3(165, 330, 165), white).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)),
new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65)) new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65))
), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height, height) .withImage(height, height)
.withFieldOfView(Math.toRadians(40)) .withFieldOfView(Math.toRadians(40))
@ -189,7 +187,7 @@ public class Examples {
var light = new DiffuseLight(new Color(7.0, 7.0, 7.0)); var light = new DiffuseLight(new Color(7.0, 7.0, 7.0));
return new Example( return new Example(
new Scene( new Scene(List.of(
new Box( new Box(
new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)), new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)),
white, white, red, green, white, null white, white, red, green, white, null
@ -203,7 +201,7 @@ public class Examples {
new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65)), new Box(new Vec3(0, 0, 0), new Vec3(165, 165, 165), white).rotateY(Math.toRadians(-18)).translate(new Vec3(130, 0, 65)),
0.01, new IsotropicMaterial(Color.WHITE) 0.01, new IsotropicMaterial(Color.WHITE)
) )
), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height, height) .withImage(height, height)
.withFieldOfView(Math.toRadians(40)) .withFieldOfView(Math.toRadians(40))
@ -223,19 +221,19 @@ public class Examples {
var aluminum = new MetallicMaterial(new Color(0.8, 0.85, 0.88)); var aluminum = new MetallicMaterial(new Color(0.8, 0.85, 0.88));
var glass = new DielectricMaterial(1.5); var glass = new DielectricMaterial(1.5);
var room = new Box(
new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)),
white, white, red, green, white, null
);
var lamp = new Parallelogram(new Vec3(343, 554, 332), new Vec3(-130, 0, 0), new Vec3(0, 0, -105), light);
var box = new Box(
new AABB(new Vec3(0, 0, 0), new Vec3(165, 330, 165)),
white, white, white, white, white, aluminum
).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295));
var sphere = new Sphere(new Vec3(190, 90, 190), 90, glass);
return new Example( return new Example(
new Scene( new Scene(List.of(room, box), List.of(lamp, sphere)),
new Box(
new AABB(new Vec3(0, 0, 0), new Vec3(555, 555, 555)),
white, white, red, green, white, null
),
new Parallelogram(new Vec3(343, 554, 332), new Vec3(-130, 0, 0), new Vec3(0, 0, -105), light),
new Box(
new AABB(new Vec3(0, 0, 0), new Vec3(165, 330, 165)),
white, white, white, white, white, aluminum
).rotateY(Math.toRadians(15)).translate(new Vec3(265, 0, 295)),
new Sphere(new Vec3(190, 90, 190), 90, glass)
),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height, height) .withImage(height, height)
.withFieldOfView(Math.toRadians(40)) .withFieldOfView(Math.toRadians(40))
@ -297,10 +295,9 @@ public class Examples {
if (height <= 0) height = 450; if (height <= 0) height = 450;
return new Example( return new Example(
new Scene( new Scene(getSkyBox(), List.of(
getSkyBox(),
new Sphere(Vec3.ZERO, 2, new LambertianMaterial(new ImageTexture("/earthmap.jpg"))) new Sphere(Vec3.ZERO, 2, new LambertianMaterial(new ImageTexture("/earthmap.jpg")))
), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height * 16 / 9, height) .withImage(height * 16 / 9, height)
.withFieldOfView(Math.toRadians(20)) .withFieldOfView(Math.toRadians(20))
@ -316,11 +313,10 @@ public class Examples {
var material = new LambertianMaterial(new PerlinTexture(4)); var material = new LambertianMaterial(new PerlinTexture(4));
return new Example( return new Example(
new Scene( new Scene(getSkyBox(), List.of(
getSkyBox(),
new Sphere(new Vec3(0, -1000, 0), 1000, material), new Sphere(new Vec3(0, -1000, 0), 1000, material),
new Sphere(new Vec3(0, 2, 0), 2, material) new Sphere(new Vec3(0, 2, 0), 2, material)
), )),
SimpleCamera.builder() SimpleCamera.builder()
.withImage(height * 16 / 9, height) .withImage(height * 16 / 9, height)
.withFieldOfView(Math.toRadians(20)) .withFieldOfView(Math.toRadians(20))

@ -170,7 +170,7 @@ public final class SimpleRenderer implements Renderer {
} }
} }
case Material.PdfScatterResult(var a, var pdf) -> { case Material.PdfScatterResult(var a, var pdf) -> {
if (scene.getLights() == null) { if (scene.getTargets() == null) {
attenuation = Color.multiply(attenuation, a); attenuation = Color.multiply(attenuation, a);
ray = new Ray(hit.position(), pdf.generate(random)); ray = new Ray(hit.position(), pdf.generate(random));
@ -178,7 +178,7 @@ public final class SimpleRenderer implements Renderer {
System.out.println(" Pdf scattering with albedo " + a); System.out.println(" Pdf scattering with albedo " + a);
} }
} else { } else {
var mixed = new MixtureProbabilityDensityFunction(new TargetingProbabilityDensityFunction(hit.position(), scene.getLights()), pdf, 0.5); var mixed = new MixtureProbabilityDensityFunction(new TargetingProbabilityDensityFunction(hit.position(), scene.getTargets()), pdf, 0.5);
var direction = mixed.generate(random); var direction = mixed.generate(random);
var idealPdf = pdf.value(direction); var idealPdf = pdf.value(direction);
@ -194,7 +194,6 @@ public final class SimpleRenderer implements Renderer {
System.out.println(" Pdf scattering with albedo " + a + " and factor " + factor); System.out.println(" Pdf scattering with albedo " + a + " and factor " + factor);
} }
} }
} }
} }

@ -4,25 +4,33 @@ import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.scene.Target; import eu.jonahbauer.raytracing.scene.Target;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.random.RandomGenerator; import java.util.random.RandomGenerator;
/** /**
* A probability density function targeting a target. * A probability density function targeting a target.
*/ */
public record TargetingProbabilityDensityFunction(@NotNull Vec3 origin, @NotNull Target target) implements ProbabilityDensityFunction { public record TargetingProbabilityDensityFunction(@NotNull Vec3 origin, @NotNull List<@NotNull Target> targets) implements ProbabilityDensityFunction {
public TargetingProbabilityDensityFunction { public TargetingProbabilityDensityFunction {
Objects.requireNonNull(origin, "origin"); Objects.requireNonNull(origin, "origin");
Objects.requireNonNull(target, "target"); targets = List.copyOf(targets);
} }
@Override @Override
public double value(@NotNull Vec3 direction) { public double value(@NotNull Vec3 direction) {
return target.getProbabilityDensity(origin, direction); var weight = 1d / targets.size();
var sum = 0.0;
for (var target : targets) {
sum += weight * target.getProbabilityDensity(origin, direction);
}
return sum;
} }
@Override @Override
public @NotNull Vec3 generate(@NotNull RandomGenerator random) { public @NotNull Vec3 generate(@NotNull RandomGenerator random) {
return target.getTargetingDirection(origin, random); return targets.get(random.nextInt(targets.size())).getTargetingDirection(origin, random);
} }
} }

@ -1,13 +1,15 @@
package eu.jonahbauer.raytracing.render.texture; package eu.jonahbauer.raytracing.render.texture;
import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.math.Vec3; import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.scene.SkyBox;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Random; import java.util.Random;
import static eu.jonahbauer.raytracing.Main.DEBUG; import static eu.jonahbauer.raytracing.Main.DEBUG;
public record Color(double r, double g, double b) implements Texture { public record Color(double r, double g, double b) implements Texture, SkyBox {
public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0); public static final @NotNull Color BLACK = new Color(0.0, 0.0, 0.0);
public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0); public static final @NotNull Color WHITE = new Color(1.0, 1.0, 1.0);
public static final @NotNull Color RED = new Color(1.0, 0.0, 0.0); public static final @NotNull Color RED = new Color(1.0, 0.0, 0.0);
@ -112,6 +114,11 @@ public record Color(double r, double g, double b) implements Texture {
return this; return this;
} }
@Override
public @NotNull Color getColor(@NotNull Ray ray) {
return this;
}
@Override @Override
public boolean isUVRequired() { public boolean isUVRequired() {
return false; return false;

@ -8,6 +8,7 @@ import eu.jonahbauer.raytracing.scene.util.HittableCollection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -15,33 +16,28 @@ public final class Scene extends HittableCollection {
private final @NotNull HittableCollection objects; private final @NotNull HittableCollection objects;
private final @NotNull SkyBox background; private final @NotNull SkyBox background;
private final @Nullable Target light; private final @Nullable List<@NotNull Target> targets;
public Scene(@NotNull List<? extends @NotNull Hittable> objects) { public Scene(@NotNull List<? extends @NotNull Hittable> objects) {
this(Color.BLACK, objects); this(objects, null);
} }
public Scene(@NotNull Color background, @NotNull List<? extends @NotNull Hittable> objects) { public Scene(@NotNull List<? extends @NotNull Hittable> objects, @Nullable List<? extends @NotNull Target> targets) {
this(SkyBox.solid(background), objects); this(Color.BLACK, objects, targets);
} }
public Scene(@NotNull SkyBox background, @NotNull List<? extends @NotNull Hittable> objects) { public Scene(@NotNull SkyBox background, @NotNull List<? extends @NotNull Hittable> objects) {
this.objects = new HittableBinaryTree(objects); this(background, objects, null);
this.background = Objects.requireNonNull(background);
this.light = (Target) objects.get(1);
}
public Scene(@NotNull Hittable @NotNull... objects) {
this(List.of(objects));
} }
public Scene(@NotNull Color background, @NotNull Hittable @NotNull... objects) { public Scene(@NotNull SkyBox background, @NotNull List<? extends @NotNull Hittable> objects, @Nullable List<? extends @NotNull Target> targets) {
this(background, List.of(objects)); var list = new ArrayList<Hittable>(objects.size() + (targets != null ? targets.size() : 0));
} list.addAll(objects);
if (targets != null) list.addAll(targets);
this.objects = new HittableBinaryTree(list);
this.background = Objects.requireNonNull(background);
public Scene(@NotNull SkyBox background, @NotNull Hittable @NotNull... objects) { this.targets = targets != null ? List.copyOf(targets) : null;
this(background, List.of(objects));
} }
@Override @Override
@ -54,8 +50,8 @@ public final class Scene extends HittableCollection {
return objects.getBoundingBox(); return objects.getBoundingBox();
} }
public @Nullable Target getLights() { public @Nullable List<@NotNull Target> getTargets() {
return light; return targets;
} }
public @NotNull Color getBackgroundColor(@NotNull Ray ray) { public @NotNull Color getBackgroundColor(@NotNull Ray ray) {

@ -18,8 +18,4 @@ public interface SkyBox {
return Color.lerp(bottom, top, alt / Math.PI + 0.5); return Color.lerp(bottom, top, alt / Math.PI + 0.5);
}; };
} }
static @NotNull SkyBox solid(@NotNull Color color) {
return _ -> color;
}
} }

Loading…
Cancel
Save