|
|
@@ -46,39 +46,7 @@ public: |
|
|
|
: m_seed(seed) |
|
|
|
{ |
|
|
|
#if 0 |
|
|
|
// Print some debug information |
|
|
|
printf("Simplex Noise of Dimension %d\n", N); |
|
|
|
|
|
|
|
long long int n = 1; for (int i = 1; i <= N; ++i) n *= i; |
|
|
|
printf(" - each hypercube cell has %lld simplices " |
|
|
|
"with %d vertices and %d edges\n", n, N + 1, N * (N + 1) / 2); |
|
|
|
|
|
|
|
vec_t<float, N> diagonal(1.f); |
|
|
|
printf(" - length of hypercube edges is 1, diagonal is %f\n", |
|
|
|
length(diagonal)); |
|
|
|
printf(" - length of parallelotope edges and diagonal is %f\n", |
|
|
|
length(unskew(diagonal))); |
|
|
|
|
|
|
|
vec_t<float, N> vertices[N + 1]; |
|
|
|
vertices[0] = vec_t<float, N>(0.f); |
|
|
|
for (int i = 0; i < N; ++i) |
|
|
|
{ |
|
|
|
vertices[i + 1] = vertices[i]; |
|
|
|
vertices[i + 1][i] += 1.f; |
|
|
|
} |
|
|
|
float minlength = 10.0f; |
|
|
|
float maxlength = 0.0f; |
|
|
|
for (int i = 0; i < N + 1; ++i) |
|
|
|
for (int j = i + 1; j < N + 1; ++j) |
|
|
|
{ |
|
|
|
float l = length(unskew(vertices[i] - vertices[j])); |
|
|
|
minlength = min(minlength, l); |
|
|
|
maxlength = max(maxlength, l); |
|
|
|
} |
|
|
|
printf(" - edge lengths between %f and %f\n", minlength, maxlength); |
|
|
|
printf(" - predicted: %f and %f\n", sqrt((float)N/(N+1)), |
|
|
|
sqrt((N+1)*(N+1)/4/(float)(N+1))); |
|
|
|
printf("\n"); |
|
|
|
debugprint(); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
@@ -99,6 +67,7 @@ public: |
|
|
|
vec_t<int, N> origin; |
|
|
|
vec_t<float, N> pos; |
|
|
|
get_origin(skew(position), origin, pos); |
|
|
|
|
|
|
|
return get_gradient(origin); |
|
|
|
} |
|
|
|
|
|
|
@@ -127,16 +96,19 @@ protected: |
|
|
|
/* “corner” will traverse the simplex along its edges in world |
|
|
|
* coordinates. */ |
|
|
|
vec_t<float, N> world_corner(0.f); |
|
|
|
float result = 0.f, coeff = 0.f; |
|
|
|
float result = 0.f, sum = 0.f; |
|
|
|
|
|
|
|
for (int i = 0; i < N + 1; ++i) |
|
|
|
{ |
|
|
|
#if 1 |
|
|
|
// FIXME: In “Noise Hardware” (2-17) Perlin uses 0.6 but Gustavson |
|
|
|
// makes an exception for dimension 2 and uses 0.5 instead. |
|
|
|
// I think m should actually increase with the dimension count. |
|
|
|
float const m = N == 2 ? 0.5f : 0.6f; |
|
|
|
float d = m - sqlength(world_pos - world_corner); |
|
|
|
// In “Noise Hardware” (2-17) Perlin uses 0.6 but Gustavson uses |
|
|
|
// 0.5 instead, saying “else the noise is not continuous at |
|
|
|
// simplex boundaries”. |
|
|
|
// And indeed, the distance between any given simplex vertex and |
|
|
|
// the opposite hyperplane is 1/sqrt(2), so the contribution of |
|
|
|
// that vertex should never be > 0 for points further than |
|
|
|
// 1/sqrt(2). Hence 0.5 - d². |
|
|
|
float d = 0.5f - sqlength(world_pos - world_corner); |
|
|
|
#else |
|
|
|
// DEBUG: this is the linear contribution of each vertex |
|
|
|
// in the skewed simplex. Unfortunately it creates artifacts. |
|
|
@@ -146,18 +118,17 @@ protected: |
|
|
|
|
|
|
|
if (d > 0) |
|
|
|
{ |
|
|
|
vec_t<float, N> const &gradient = get_gradient(origin); |
|
|
|
|
|
|
|
// Perlin uses 8d⁴ whereas Gustavson uses d⁴ and a final |
|
|
|
// multiplication factor at the end. |
|
|
|
//d = 8.f * d * d * d * d; |
|
|
|
// multiplication factor at the end. Let’s go with |
|
|
|
// Gustavson, it’s a few multiplications less. |
|
|
|
d = d * d * d * d; |
|
|
|
|
|
|
|
//d = (3.f - 2.f * d) * d * d; |
|
|
|
//d = ((6 * d - 15) * d + 10) * d * d * d; |
|
|
|
|
|
|
|
result += d * dot(gradient, world_pos - world_corner); |
|
|
|
coeff += d; |
|
|
|
result += d * dot(get_gradient(origin), |
|
|
|
world_pos - world_corner); |
|
|
|
sum += d; |
|
|
|
} |
|
|
|
|
|
|
|
if (i < N) |
|
|
@@ -170,10 +141,14 @@ protected: |
|
|
|
} |
|
|
|
|
|
|
|
// FIXME: Gustavson uses the value 70 for dimension 2, 32 for |
|
|
|
// dimension 3, and 27 for dimension 4; find where this comes from |
|
|
|
// and maybe find a reasonable formula. |
|
|
|
//return 70.f * result / coeff; |
|
|
|
float const k = N == 2 ? 70 : N == 3 ? 32 : N == 4 ? 27 : 20; |
|
|
|
// dimension 3, and 27 for dimension 4, and uses non-unit gradients |
|
|
|
// of length sqrt(2), sqrt(2) and sqrt(3). Find out where this comes |
|
|
|
// from and maybe find a more generic formula. |
|
|
|
float const k = N == 2 ? (70.f * sqrt(2.f)) // approx. 99 |
|
|
|
: N == 3 ? (70.f * sqrt(2.f)) // approx. 45 |
|
|
|
: N == 4 ? (70.f * sqrt(3.f)) // approx. 47 |
|
|
|
: 50.f; |
|
|
|
//return k * result / sum; |
|
|
|
return k * result; |
|
|
|
} |
|
|
|
|
|
|
@@ -255,15 +230,126 @@ protected: |
|
|
|
|
|
|
|
/* For a given world position, extract grid coordinates (origin) and |
|
|
|
* the corresponding delta position (pos). */ |
|
|
|
inline void get_origin(vec_t<float, N> const & simplex_position, |
|
|
|
inline void get_origin(vec_t<float, N> const & world_position, |
|
|
|
vec_t<int, N> & origin, vec_t<float, N> & pos) const |
|
|
|
{ |
|
|
|
// Finding floor point index |
|
|
|
for (int i = 0; i < N; ++i) |
|
|
|
origin[i] = ((int) simplex_position[i]) - (simplex_position[i] < 0 ? 1 : 0); |
|
|
|
origin[i] = ((int)world_position[i]) - (world_position[i] < 0); |
|
|
|
|
|
|
|
// Extracting decimal part from simplex sample |
|
|
|
pos = simplex_position - (vec_t<float, N>)origin; |
|
|
|
pos = world_position - (vec_t<float, N>)origin; |
|
|
|
} |
|
|
|
|
|
|
|
private: |
|
|
|
void debugprint() |
|
|
|
{ |
|
|
|
// Print some debug information |
|
|
|
printf("Simplex Noise of Dimension %d\n", N); |
|
|
|
|
|
|
|
long long int n = 1; for (int i = 1; i <= N; ++i) n *= i; |
|
|
|
printf(" - each hypercube cell has %lld simplices " |
|
|
|
"with %d vertices and %d edges\n", n, N + 1, N * (N + 1) / 2); |
|
|
|
|
|
|
|
vec_t<float, N> diagonal(1.f); |
|
|
|
printf(" - regular hypercube:\n"); |
|
|
|
printf(" · edge length 1\n"); |
|
|
|
printf(" · diagonal length %f\n", length(diagonal)); |
|
|
|
printf(" - unskewed parallelotope:\n"); |
|
|
|
printf(" · edge length %f\n", length(unskew(diagonal))); |
|
|
|
printf(" · diagonal length %f\n", length(unskew(diagonal))); |
|
|
|
printf(" · simplex edge lengths between %f and %f\n", |
|
|
|
sqrt((float)N/(N+1)), sqrt((N+1)*(N+1)/4/(float)(N+1))); |
|
|
|
|
|
|
|
/* Generate simplex vertices */ |
|
|
|
vec_t<float, N> vertices[N + 1]; |
|
|
|
vertices[0] = vec_t<float, N>(0.f); |
|
|
|
for (int i = 0; i < N; ++i) |
|
|
|
{ |
|
|
|
vertices[i + 1] = vertices[i]; |
|
|
|
vertices[i + 1][i] += 1.f; |
|
|
|
} |
|
|
|
for (int i = 0; i < N + 1; ++i) |
|
|
|
vertices[i] = unskew(vertices[i]); |
|
|
|
|
|
|
|
for (int i = 0; i < N + 1; ++i) |
|
|
|
{ |
|
|
|
printf(" - vertex %d\n", i); |
|
|
|
|
|
|
|
#if 0 |
|
|
|
/* Coordinates for debugging purposes */ |
|
|
|
printf(" · ["); |
|
|
|
for (int k = 0; k < N; ++k) |
|
|
|
printf(" %f", vertices[i][k]); |
|
|
|
printf(" ]\n"); |
|
|
|
#endif |
|
|
|
|
|
|
|
/* Analyze edge lengths from that vertex */ |
|
|
|
float minlength = 1.0f; |
|
|
|
float maxlength = 0.0f; |
|
|
|
for (int j = 0; j < N + 1; ++j) |
|
|
|
{ |
|
|
|
if (i == j) |
|
|
|
continue; |
|
|
|
|
|
|
|
float l = length(vertices[i] - vertices[j]); |
|
|
|
minlength = min(minlength, l); |
|
|
|
maxlength = max(maxlength, l); |
|
|
|
} |
|
|
|
printf(" · edge lengths between %f and %f\n", |
|
|
|
minlength, maxlength); |
|
|
|
|
|
|
|
#if 0 |
|
|
|
/* Experimental calculation of the distance to the opposite |
|
|
|
* hyperplane, by picking random points. Works reasonably |
|
|
|
* well up to dimension 6. After that, we’d need something |
|
|
|
* better such as gradient walk. */ |
|
|
|
float mindist = 1.0f; |
|
|
|
for (int run = 0; run < 10000000; ++run) |
|
|
|
{ |
|
|
|
vec_t<float, N> p(0.f); |
|
|
|
float sum = 0.f; |
|
|
|
for (int j = 0; j < N + 1; ++j) |
|
|
|
{ |
|
|
|
if (i == j) |
|
|
|
continue; |
|
|
|
float k = rand(1.f); |
|
|
|
p += k * vertices[j]; |
|
|
|
sum += k; |
|
|
|
} |
|
|
|
mindist = min(mindist, distance(vertices[i], p / sum)); |
|
|
|
} |
|
|
|
printf(" · approx. dist. to opposite hyperplane: %f\n", mindist); |
|
|
|
#endif |
|
|
|
|
|
|
|
#if 0 |
|
|
|
/* Find a normal vector to the opposite hyperplane. First, pick |
|
|
|
* any point p(i0) on the hyperplane. We just need i0 != i. Then, |
|
|
|
* build a matrix where each row is p(i0)p(j) for all j != i0. |
|
|
|
* Multiplying this matrix by the normal vectors gives a vector |
|
|
|
* full of zeroes except at position i. So we build a vector |
|
|
|
* full of zeroes except at position i, and multiply it by the |
|
|
|
* matrix inverse. */ |
|
|
|
int i0 = (i == 0) ? 1 : 0; |
|
|
|
mat_t<float, N, N> m; |
|
|
|
for (int j = 0; j < N; ++j) |
|
|
|
{ |
|
|
|
auto v = vertices[j < i0 ? j : j + 1] - vertices[i0]; |
|
|
|
for (int k = 0; k < N; ++k) |
|
|
|
m[k][j] = v[k]; |
|
|
|
} |
|
|
|
vec_t<float, N> p(0.f); |
|
|
|
p[i < i0 ? i : i - 1] = 1.f; |
|
|
|
auto normal = normalize(inverse(m) * p); |
|
|
|
|
|
|
|
/* Find distance from current vertex to the opposite hyperplane. |
|
|
|
* Just use the projection theorem in N dimensions. */ |
|
|
|
auto w = vertices[i] - vertices[i0]; |
|
|
|
float dist = abs(dot(normal, w)); |
|
|
|
printf(" · distance to opposite hyperplane: %f\n", dist); |
|
|
|
#endif |
|
|
|
} |
|
|
|
printf("\n"); |
|
|
|
} |
|
|
|
|
|
|
|
/* A user-provided random seed. Defaults to zero. */ |
|
|
|