add perlin noise texture

main
jbb01 6 months ago
parent e6447fe684
commit 70f2f38e96

@ -6,6 +6,7 @@ import eu.jonahbauer.raytracing.render.texture.Color;
import eu.jonahbauer.raytracing.render.camera.SimpleCamera; import eu.jonahbauer.raytracing.render.camera.SimpleCamera;
import eu.jonahbauer.raytracing.render.material.*; import eu.jonahbauer.raytracing.render.material.*;
import eu.jonahbauer.raytracing.render.texture.ImageTexture; import eu.jonahbauer.raytracing.render.texture.ImageTexture;
import eu.jonahbauer.raytracing.render.texture.PerlinTexture;
import eu.jonahbauer.raytracing.scene.Hittable; import eu.jonahbauer.raytracing.scene.Hittable;
import eu.jonahbauer.raytracing.scene.Scene; import eu.jonahbauer.raytracing.scene.Scene;
import eu.jonahbauer.raytracing.scene.SkyBox; import eu.jonahbauer.raytracing.scene.SkyBox;
@ -34,6 +35,7 @@ public class Examples {
register("CORNELL_SMOKE", Examples::getCornellBoxSmoke); register("CORNELL_SMOKE", Examples::getCornellBoxSmoke);
register("DIAGRAMM", Examples::getDiagramm); register("DIAGRAMM", Examples::getDiagramm);
register("EARTH", Examples::getEarth); register("EARTH", Examples::getEarth);
register("PERLIN", Examples::getPerlin);
} }
public static @NotNull IntFunction<Example> getByName(@NotNull String name) { public static @NotNull IntFunction<Example> getByName(@NotNull String name) {
@ -274,6 +276,26 @@ public class Examples {
); );
} }
public static @NotNull Example getPerlin(int height) {
if (height <= 0) height = 450;
var material = new LambertianMaterial(new PerlinTexture(4));
return new Example(
new Scene(
getSkyBox(),
new Sphere(new Vec3(0, -1000, 0), 1000, material),
new Sphere(new Vec3(0, 2, 0), 2, material)
),
SimpleCamera.builder()
.withImage(height * 16 / 9, height)
.withFieldOfView(Math.toRadians(20))
.withPosition(new Vec3(13, 2, 3))
.withTarget(Vec3.ZERO)
.build()
);
}
private static @NotNull SkyBox getSkyBox() { private static @NotNull SkyBox getSkyBox() {
return SkyBox.gradient(new Color(0.5, 0.7, 1.0), Color.WHITE); return SkyBox.gradient(new Color(0.5, 0.7, 1.0), Color.WHITE);
} }

@ -0,0 +1,146 @@
package eu.jonahbauer.raytracing.render.texture;
import eu.jonahbauer.raytracing.math.Vec3;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Random;
import java.util.function.DoubleFunction;
import java.util.random.RandomGenerator;
public final class PerlinTexture implements Texture {
private static final int POINT_COUNT = 256;
private static final @NotNull Random RANDOM = new Random();
private static final @NotNull DoubleFunction<Color> GREYSCALE = t -> new Color(t, t, t);
private final double scale;
private final int turbulence;
private final @NotNull DoubleFunction<Color> color;
private final int mask;
private final Vec3[] randvec;
private final int[] permX;
private final int[] permY;
private final int[] permZ;
public PerlinTexture() {
this(1.0);
}
public PerlinTexture(double scale) {
this(scale, 7);
}
public PerlinTexture(double scale, int turbulence) {
this(scale, turbulence, GREYSCALE);
}
public PerlinTexture(double scale, int turbulence, @NotNull DoubleFunction<Color> color) {
this(scale, turbulence, color, POINT_COUNT, RANDOM);
}
public PerlinTexture(
double scale, int turbulence, @NotNull DoubleFunction<Color> color,
int count, @NotNull RandomGenerator random
) {
if ((count & (count - 1)) != 0) throw new IllegalArgumentException("count must be a power of two");
if (turbulence <= 0) throw new IllegalArgumentException("turbulence must be positive");
this.scale = scale;
this.turbulence = turbulence;
this.color = Objects.requireNonNull(color, "color");
this.mask = count - 1;
this.randvec = new Vec3[count];
for (int i = 0; i < count; i++) {
this.randvec[i] = Vec3.random(random, true);
}
this.permX = generatePerm(count, random);
this.permY = generatePerm(count, random);
this.permZ = generatePerm(count, random);
}
private static int @NotNull[] generatePerm(int count, @NotNull RandomGenerator random) {
int[] p = new int[count];
for (int i = 0; i < count; i++) {
p[i] = i;
}
permutate(p, random);
return p;
}
private static void permutate(int @NotNull[] p, @NotNull RandomGenerator random) {
for (int i = p.length - 1; i > 0; i--) {
int target = random.nextInt(i);
int tmp = p[i];
p[i] = p[target];
p[target] = tmp;
}
}
public double getNoise(@NotNull Vec3 p) {
var x = p.x() * scale;
var y = p.y() * scale;
var z = p.z() * scale;
var u = x - Math.floor(x);
var v = y - Math.floor(y);
var w = z - Math.floor(z);
int i = (int) Math.floor(x);
int j = (int) Math.floor(y);
int k = (int) Math.floor(z);
var c = new Vec3[2][2][2];
for (int di = 0; di < 2; di++) {
for (int dj = 0; dj < 2; dj++) {
for (int dk = 0; dk < 2; dk++) {
c[di][dj][dk] = randvec[permX[(i + di) & mask] ^ permY[(j + dj) & mask] ^ permZ[(k + dk) & mask]];
}
}
}
return interpolate(c, u, v, w);
}
public double getNoise(@NotNull Vec3 p, int depth) {
var accum = 0.0;
var temp = p;
var weight = 1.0;
for (int i = 0; i < depth; i++) {
accum += weight * getNoise(temp);
weight *= 0.5;
temp = temp.times(2);
}
return accum;
}
@Override
public @NotNull Color get(double u, double v, @NotNull Vec3 p) {
var noise = getNoise(p, turbulence);
var t = Math.fma(0.5, Math.sin(Math.PI * noise), 0.5);
return color.apply(t);
}
private static double interpolate(Vec3[][][] c, double u, double v, double w) {
var uu = u * u * (3 - 2 * u);
var vv = v * v * (3 - 2 * v);
var ww = w * w * (3 - 2 * w);
var accum = 0.0;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
var weight = new Vec3(u - i, v - j, w - k);
accum += (i * uu + (1 - i) * (1 - uu))
* (j * vv + (1 - j) * (1 - vv))
* (k * ww + (1 - k) * (1 - ww))
* c[i][j][k].times(weight);
}
}
}
return accum;
}
}
Loading…
Cancel
Save