add perlin noise texture
parent
e6447fe684
commit
70f2f38e96
@ -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…
Reference in New Issue