add perlin noise texture
This commit is contained in:
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…
x
Reference in New Issue
Block a user