Back Effekte hinzugefügt

This commit is contained in:
2026-08-09 22:22:44 +02:00
parent 46be9ffe57
commit 0a657a5831
7 changed files with 220 additions and 7 deletions

View File

@@ -682,4 +682,172 @@ public final class MarchingCubes {
return chunk.getDensity(x, y, z);
}
/**
* Verschiebt Vertices auf steilen Flächen (normal.y < 0.707 ≈ 45°) horizontal (XZ)
* per kohärentem Value-Noise. Macht Klippen natürlicher und felsiger.
* Rand-Vertices (Chunk-Grenzen) bleiben fixiert, Nahtlosigkeit bleibt erhalten.
*
* @param strength Amplitude: 0 = kein Effekt, 1 = bis ~2 m maximale Verschiebung
* @param worldX Weltkoordinate X des Chunk-Ursprungs (cx * CELLS)
* @param worldZ Weltkoordinate Z des Chunk-Ursprungs (cz * CELLS)
*/
public static Mesh perturb(Mesh mesh, float strength, float worldX, float worldZ) {
if (mesh == null || strength < 0.001f) return mesh;
FloatBuffer posF = mesh.getFloatBuffer(VertexBuffer.Type.Position);
if (posF == null) return mesh;
int vertCount = posF.capacity() / 3;
if (vertCount < 3) return mesh;
int triCount = vertCount / 3;
float[] pos = new float[vertCount * 3];
posF.rewind(); posF.get(pos);
// Vertex-Gruppen aufbauen (gleiche Position → gleiche Gruppe)
HashMap<Long, Integer> keyToGroup = new HashMap<>(vertCount / 3 + 16);
int[] vertGroup = new int[vertCount];
int[] groupFirst = new int[vertCount];
int groupCount = 0;
for (int v = 0; v < vertCount; v++) {
long key = posKey(pos, v);
Integer g = keyToGroup.get(key);
if (g == null) {
keyToGroup.put(key, groupCount);
groupFirst[groupCount] = v;
vertGroup[v] = groupCount++;
} else {
vertGroup[v] = g;
}
}
float[] gx = new float[groupCount];
float[] gy = new float[groupCount];
float[] gz = new float[groupCount];
for (int g = 0; g < groupCount; g++) {
int v = groupFirst[g];
gx[g] = pos[v*3]; gy[g] = pos[v*3+1]; gz[g] = pos[v*3+2];
}
// Rand-Vertices einfrieren (Chunk-Nahtlosigkeit)
float bound = VoxelChunk.CELLS;
boolean[] pinned = new boolean[groupCount];
for (int g = 0; g < groupCount; g++) {
float x = gx[g], y = gy[g], z = gz[g];
if (x < 0.01f || x > bound - 0.01f ||
y < 0.01f || y > bound - 0.01f ||
z < 0.01f || z > bound - 0.01f) {
pinned[g] = true;
}
}
// Flächennormalen berechnen und pro Gruppe akkumulieren
float[] gnx = new float[groupCount];
float[] gny = new float[groupCount];
float[] gnz = new float[groupCount];
for (int t = 0; t < triCount; t++) {
int g0=vertGroup[t*3], g1=vertGroup[t*3+1], g2=vertGroup[t*3+2];
float p0x=pos[t*9], p0y=pos[t*9+1], p0z=pos[t*9+2];
float p1x=pos[t*9+3], p1y=pos[t*9+4], p1z=pos[t*9+5];
float p2x=pos[t*9+6], p2y=pos[t*9+7], p2z=pos[t*9+8];
float ex=p1x-p0x, ey=p1y-p0y, ez=p1z-p0z;
float fx=p2x-p0x, fy=p2y-p0y, fz=p2z-p0z;
float nx=ey*fz-ez*fy, ny=ez*fx-ex*fz, nz=ex*fy-ey*fx;
gnx[g0]+=nx; gny[g0]+=ny; gnz[g0]+=nz;
gnx[g1]+=nx; gny[g1]+=ny; gnz[g1]+=nz;
gnx[g2]+=nx; gny[g2]+=ny; gnz[g2]+=nz;
}
for (int g = 0; g < groupCount; g++) {
float len=(float)Math.sqrt(gnx[g]*gnx[g]+gny[g]*gny[g]+gnz[g]*gnz[g]);
if (len > 1e-6f) { gnx[g]/=len; gny[g]/=len; gnz[g]/=len; }
else { gny[g] = 1f; }
}
// Horizontale Verschiebung auf steile Gruppen anwenden.
// fBm (Fractal Brownian Motion) mit 4 Oktaven: gleiche Frequenz auf allen
// drei Achsen → jedes Höhenniveau bekommt eigene, unabhängige Verschiebung.
// Zwei unkorrelierte fBm-Felder für X und Z (unterschiedliche Offsets).
final float STEEP = 0.707f; // cos(45°)
final float MAX_D = 4.0f; // m maximale Verschiebung bei strength=1, senkrechter Fläche
for (int g = 0; g < groupCount; g++) {
if (pinned[g]) continue;
float ny = gny[g];
if (ny >= STEEP) continue;
float steepness = 1f - ny / STEEP; // 0 bei 45°, 1 bei senkrecht
float wx = worldX + gx[g];
float wz = worldZ + gz[g];
float wy = gy[g];
float noiseX = perturbFbm(wx * 0.08f, wy * 0.08f, wz * 0.08f);
float noiseZ = perturbFbm(wx * 0.08f + 31.7f, wy * 0.08f + 11.3f, wz * 0.08f + 67.1f);
float disp = steepness * strength * MAX_D;
gx[g] += noiseX * disp;
gz[g] += noiseZ * disp;
}
// Positionen in Buffer schreiben
for (int v = 0; v < vertCount; v++) {
int g = vertGroup[v];
pos[v*3] = gx[g]; pos[v*3+2] = gz[g];
}
posF.rewind(); posF.put(pos); posF.rewind();
// Normalen aus geänderter Geometrie neu berechnen
java.util.Arrays.fill(gnx, 0f); java.util.Arrays.fill(gny, 0f); java.util.Arrays.fill(gnz, 0f);
for (int t = 0; t < triCount; t++) {
int g0=vertGroup[t*3], g1=vertGroup[t*3+1], g2=vertGroup[t*3+2];
float p0x=pos[t*9], p0y=pos[t*9+1], p0z=pos[t*9+2];
float p1x=pos[t*9+3], p1y=pos[t*9+4], p1z=pos[t*9+5];
float p2x=pos[t*9+6], p2y=pos[t*9+7], p2z=pos[t*9+8];
float ex=p1x-p0x, ey=p1y-p0y, ez=p1z-p0z;
float fx=p2x-p0x, fy=p2y-p0y, fz=p2z-p0z;
float nx=ey*fz-ez*fy, ny=ez*fx-ex*fz, nz=ex*fy-ey*fx;
gnx[g0]+=nx; gny[g0]+=ny; gnz[g0]+=nz;
gnx[g1]+=nx; gny[g1]+=ny; gnz[g1]+=nz;
gnx[g2]+=nx; gny[g2]+=ny; gnz[g2]+=nz;
}
FloatBuffer normF = mesh.getFloatBuffer(VertexBuffer.Type.Normal);
if (normF != null) {
normF.rewind();
for (int v = 0; v < vertCount; v++) {
int g = vertGroup[v];
float len=(float)Math.sqrt(gnx[g]*gnx[g]+gny[g]*gny[g]+gnz[g]*gnz[g]);
if (len > 1e-6f) normF.put(gnx[g]/len).put(gny[g]/len).put(gnz[g]/len);
else normF.put(0f).put(1f).put(0f);
}
normF.rewind();
}
mesh.updateBound();
return mesh;
}
/** fBm (Fractal Brownian Motion), 4 Oktaven, Ergebnis in [-1, 1]. */
private static float perturbFbm(float x, float y, float z) {
float v = 0f, amp = 1f, sumAmp = 0f;
for (int i = 0; i < 4; i++) {
v += perturbNoise(x, y, z) * amp;
sumAmp += amp;
x *= 2.1f; y *= 2.1f; z *= 2.1f;
amp *= 0.5f;
}
return v / sumAmp;
}
/** Trilinear interpoliertes Value-Noise in [-1, 1]. Kohärent, deterministisch. */
private static float perturbNoise(float x, float y, float z) {
int ix=(int)Math.floor(x), iy=(int)Math.floor(y), iz=(int)Math.floor(z);
float fx=x-ix, fy=y-iy, fz=z-iz;
fx=fx*fx*(3-2*fx); fy=fy*fy*(3-2*fy); fz=fz*fz*(3-2*fz);
float v00 = perturbHash(ix,iy,iz) + fx*(perturbHash(ix+1,iy,iz) - perturbHash(ix,iy,iz));
float v10 = perturbHash(ix,iy+1,iz) + fx*(perturbHash(ix+1,iy+1,iz) - perturbHash(ix,iy+1,iz));
float v01 = perturbHash(ix,iy,iz+1) + fx*(perturbHash(ix+1,iy,iz+1) - perturbHash(ix,iy,iz+1));
float v11 = perturbHash(ix,iy+1,iz+1)+fx*(perturbHash(ix+1,iy+1,iz+1)-perturbHash(ix,iy+1,iz+1));
return (v00 + fy*(v10-v00)) + fz*((v01 + fy*(v11-v01)) - (v00 + fy*(v10-v00)));
}
private static float perturbHash(int x, int y, int z) {
int h = x * 374761393 + y * 1103515245 + z * -2012135261;
h ^= (h >>> 15); h *= 0x9e3779b9; h ^= (h >>> 12);
return ((h >>> 1) & 0xFFFF) * (2f / 65535f) - 1f;
}
}