109 lines
3.2 KiB
C++
109 lines
3.2 KiB
C++
#include <random>
|
|
#include <algorithm>
|
|
#include "perlinnoise.h"
|
|
|
|
Vector3d PerlinNoise::randomGradient()
|
|
{
|
|
Vector3d v;
|
|
v.x = distribution(generator);
|
|
v.y = distribution(generator);
|
|
v.z = distribution(generator);
|
|
return normalized(v);
|
|
}
|
|
|
|
float PerlinNoise::getGradientValue(int x, int y, int z)
|
|
{
|
|
Vector3d position = Vector3d(x, y, z);
|
|
|
|
// Translate position to grid
|
|
position /= (float) size;
|
|
position *= (float) gridSize;
|
|
|
|
// Get the grid coordinates
|
|
int x_min = floor(position.x);
|
|
int y_min = floor(position.y);
|
|
int z_min = floor(position.z);
|
|
|
|
// Get the grid coordinates of the next grid point
|
|
int x_max = x_min + 1;
|
|
int y_max = y_min + 1;
|
|
int z_max = z_min + 1;
|
|
|
|
// Get the fractional part of the position
|
|
Vector3d delta = position - Vector3d(x_min, y_min, z_min);
|
|
|
|
// Interpolate the gradient values
|
|
float n000 = getCornerValue(position, Vector3d(x_min, y_min, z_min));
|
|
float n100 = getCornerValue(position, Vector3d(x_max, y_min, z_min));
|
|
float n010 = getCornerValue(position, Vector3d(x_min, y_max, z_min));
|
|
float n110 = getCornerValue(position, Vector3d(x_max, y_max, z_min));
|
|
float n001 = getCornerValue(position, Vector3d(x_min, y_min, z_max));
|
|
float n101 = getCornerValue(position, Vector3d(x_max, y_min, z_max));
|
|
float n011 = getCornerValue(position, Vector3d(x_min, y_max, z_max));
|
|
float n111 = getCornerValue(position, Vector3d(x_max, y_max, z_max));
|
|
|
|
// Smooth the interpolation
|
|
float ix0 = interpolate(n000, n100, delta.x);
|
|
float ix1 = interpolate(n010, n110, delta.x);
|
|
float ix2 = interpolate(n001, n101, delta.x);
|
|
float ix3 = interpolate(n011, n111, delta.x);
|
|
|
|
float iy0 = interpolate(ix0, ix1, delta.y);
|
|
float iy1 = interpolate(ix2, ix3, delta.y);
|
|
|
|
float noise = interpolate(iy0, iy1, delta.z);
|
|
|
|
return noise;
|
|
}
|
|
|
|
PerlinNoise::PerlinNoise(int size, int gridSize) : Noise(size), gridSize(gridSize)
|
|
{
|
|
// Init random
|
|
std::random_device rd;
|
|
this->generator = std::mt19937(rd());
|
|
this->distribution = std::normal_distribution<float>(0.0f, 1.0f);
|
|
|
|
generateNoise();
|
|
}
|
|
|
|
void PerlinNoise::generateNoise()
|
|
{
|
|
// Generate gradients
|
|
gradients.clear();
|
|
gradients.resize(pow(gridSize, 3));
|
|
for (int i = 0; i < gradients.size(); i++)
|
|
{
|
|
gradients[i] = randomGradient();
|
|
}
|
|
|
|
// Generate each pixel
|
|
for (int x = 0; x < size; x++)
|
|
{
|
|
for (int y = 0; y < size; y++)
|
|
{
|
|
for (int z = 0; z < size; z++)
|
|
{
|
|
setNoise(x, y, z, getGradientValue(x, y, z));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Normalize cloudNoise map to [0, 1]
|
|
float min = *std::min_element(noiseMap.begin(), noiseMap.end());
|
|
float max = *std::max_element(noiseMap.begin(), noiseMap.end());
|
|
|
|
std::for_each(noiseMap.begin(), noiseMap.end(), [min, max](float &value)
|
|
{
|
|
value = (value - min) / (max - min);
|
|
});
|
|
}
|
|
|
|
float PerlinNoise::getCornerValue(Vector3d position, Vector3d corner)
|
|
{
|
|
int x = (int) corner.x % gridSize;
|
|
int y = (int) corner.y % gridSize;
|
|
int z = (int) corner.z % gridSize;
|
|
|
|
Vector3d gradient = gradients[x + y * gridSize + z * gridSize * gridSize];
|
|
return dotProduct(gradient, position - corner);
|
|
}
|