It's a rant/question thing, simplex noise is a noise type created by Ken Perlin to address the shortcomings of Perlin noise.
It's complicated math, I don't understand it, it doesn't cooperate and I don't find this entertaining at all, here's the relevant code if anyone wants to take a stab at how to get useable integers in the range of 0-n
Code: Select all
float scaled_octave_noise_3d( const float octaves, const float persistence, const float scale, const float loBound, const float hiBound, const float x, const float y, const float z ) {
return octave_noise_3d(octaves, persistence, scale, x, y, z) * (hiBound - loBound) / 2 + (hiBound + loBound) / 2;
}
float octave_noise_3d( const float octaves, const float persistence, const float scale, const float x, const float y, const float z ) {
float total = 0;
float frequency = scale;
float amplitude = 1;
// We have to keep track of the largest possible amplitude,
// because each octave adds more, and we need a value in [-1, 1].
float maxAmplitude = 0;
for( int i=0; i < octaves; i++ ) {
total += raw_noise_3d( x * frequency, y * frequency, z * frequency ) * amplitude;
frequency *= 2;
maxAmplitude += amplitude;
amplitude *= persistence;
}
return total / maxAmplitude;
}
float raw_noise_3d( const float x, const float y, const float z ) {
float n0, n1, n2, n3; // Noise contributions from the four corners
// Skew the input space to determine which simplex cell we're in
float F3 = 1.0/3.0;
float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D
int i = fastfloor(x+s);
int j = fastfloor(y+s);
int k = fastfloor(z+s);
float G3 = 1.0/6.0; // Very nice and simple unskew factor, too
float t = (i+j+k)*G3;
float X0 = i-t; // Unskew the cell origin back to (x,y,z) space
float Y0 = j-t;
float Z0 = k-t;
float x0 = x-X0; // The x,y,z distances from the cell origin
float y0 = y-Y0;
float z0 = z-Z0;
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
if(x0>=y0) {
if(y0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order
else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order
}
else { // x0<y0
if(y0<z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Z Y X order
else if(x0<z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y Z X order
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } // Y X Z order
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
// c = 1/6.
float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
float y1 = y0 - j1 + G3;
float z1 = z0 - k1 + G3;
float x2 = x0 - i2 + 2.0*G3; // Offsets for third corner in (x,y,z) coords
float y2 = y0 - j2 + 2.0*G3;
float z2 = z0 - k2 + 2.0*G3;
float x3 = x0 - 1.0 + 3.0*G3; // Offsets for last corner in (x,y,z) coords
float y3 = y0 - 1.0 + 3.0*G3;
float z3 = z0 - 1.0 + 3.0*G3;
// Work out the hashed gradient indices of the four simplex corners
int ii = i & 255;
int jj = j & 255;
int kk = k & 255;
int gi0 = nPerm[ii+nPerm[jj+nPerm[kk]]] % 12;
int gi1 = nPerm[ii+i1+nPerm[jj+j1+nPerm[kk+k1]]] % 12;
int gi2 = nPerm[ii+i2+nPerm[jj+j2+nPerm[kk+k2]]] % 12;
int gi3 = nPerm[ii+1+nPerm[jj+1+nPerm[kk+1]]] % 12;
// Calculate the contribution from the four corners
float t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
if(t0<0) n0 = 0.0;
else {
t0 *= t0;
n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);
}
float t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
if(t1<0) n1 = 0.0;
else {
t1 *= t1;
n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);
}
float t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
if(t2<0) n2 = 0.0;
else {
t2 *= t2;
n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);
}
float t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
if(t3<0) n3 = 0.0;
else {
t3 *= t3;
n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return 32.0*(n0 + n1 + n2 + n3);
}
The functions have been sorted in order of when they're called, the scaled octave noise calls octave noise which calls raw noise (octave noise generates many octaves of raw noise, scaled octave noise then clamps it within values, to my understanding).
Code: Select all
float terrainGen::noiseGen3D( float loBound, float hiBound, int algo, float octaves, float persistence, int_least64_t x, int_least64_t y, int_least64_t z, float nScale)
{
switch(algo)
{
case 0: return octave_noise_3d ( octaves, persistence, nScale, x, y, z );
break; //3D octave simplex
case 1: return scaled_octave_noise_3d ( octaves, persistence, nScale, loBound,
hiBound, x, y, z );
break; //3D scaled octave simplex
case 2: return scaled_raw_noise_3d ( loBound, hiBound, x, y, z );
break; //3D scaled raw noise
case 3: return raw_noise_3d( x, y, z );
break; //3D raw noise
}
}
//loBound, hiBound, algo (0-3), octaves, persistence, x, y, z, scale
/*
* Specifically what they control is as follows:
* loBound/hiBound: the range of values that can be generated, not sure how it works.
* algo picks between the algorithms, octave, scaled octave, scaled raw, raw.
* octaves: not sure exactly but I think it determines how many noise samles are averaged together
* persistence: determines the smoothness of the curve. lower is smoother
* x, y, z: coordinates, pls leave them as is desu.
* scale: modulates the scale of the wavelength, lower value means lower frequency
*/
b_blocks[x][y][z].setBlock(static_cast<int>(tGen->noiseGen3D(1.0f, 3.0f, 1, 3.1415265935f, 0.55f, x, y, z, 0.03f)))
tGen is just an instance of terrainGen, the only relevant function is called (as terrain gen is just a container for the noise function to be used by the terrain related classes).
Essentially the issue here is that I don't understand how to get useable noise out of this that makes any bloody sense, the math is above me (as is most of mr. Perlin's work, you should see the reference implementation, many a programmer far greater than I'll ever be scratch their head on that one).
EDIT: On that matter I'd still need to figure out how to scale it on the y axis to get a lower density the higher we get (the idea being that the lower we get the lower the density gets, the lower the density gets the more likely air becomes, at 0,0,0 the density should be really low so that above you there's sky with only the occasional mountain peak sticking up and below you there's ground with only the occasional cave, ravine etc.
This math is going to be the end of me, I don't understand simplicies and everything about simplex noise is far above my head.
EDIT2: With further experimentation I just get more useless abstract results,
That's a cube with three solid sides and that is lacking three sides, there are a few ways this chunk could theoretically render but all methods imply absolutely messed up noise that's nothing like terrain.
EDIT3:
now that's a bit better, the range is still super weird though
and the range is still FUBAR, currently I'm downsampling the values with a division by 10, which just drops the values from 10-12 to 0-2
I want a range from 0-n (testing with n = 32 right now) but can't seem to find any case that generates these at all.
EDIT4:
Based on what I can tell the output is always going to be in a range like this, meaning I can interpolate the output and treat it as a density value, in which case... I'd still need to somehow scale it along the y axis to be able to generate useful terrain from this, however I'm not entirely sure where this scaling would logically occur, currently the noise is uniform, not particularly weighted towards any particular axis having a lower expected noise the higher that number goes.