2023-01-24 06:26:24 +01:00
|
|
|
#include <random>
|
|
|
|
#include <algorithm>
|
2023-01-29 16:27:15 +01:00
|
|
|
#include <thread>
|
|
|
|
#include <iostream>
|
2023-01-24 05:22:40 +01:00
|
|
|
#include "perlinnoise.h"
|
2023-01-24 06:26:24 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-01-29 12:19:19 +01:00
|
|
|
PerlinNoise::PerlinNoise(int size, int gridSize, unsigned int seed) : Noise(size), gridSize(gridSize)
|
2023-01-24 06:26:24 +01:00
|
|
|
{
|
|
|
|
// Init random
|
2023-01-29 12:19:19 +01:00
|
|
|
if (seed == 0)
|
|
|
|
{
|
|
|
|
std::random_device rd;
|
|
|
|
seed = rd();
|
|
|
|
}
|
|
|
|
this->generator = std::mt19937(seed);
|
2023-01-24 06:26:24 +01:00
|
|
|
this->distribution = std::normal_distribution<float>(0.0f, 1.0f);
|
|
|
|
|
|
|
|
generateNoise();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PerlinNoise::generateNoise()
|
|
|
|
{
|
2023-01-29 16:27:15 +01:00
|
|
|
auto start = std::chrono::high_resolution_clock::now();
|
|
|
|
|
2023-01-24 06:26:24 +01:00
|
|
|
// Generate gradients
|
|
|
|
gradients.clear();
|
|
|
|
gradients.resize(pow(gridSize, 3));
|
|
|
|
for (int i = 0; i < gradients.size(); i++)
|
|
|
|
{
|
|
|
|
gradients[i] = randomGradient();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate each pixel
|
2023-01-29 16:27:15 +01:00
|
|
|
int const nThreads = (int) std::thread::hardware_concurrency() - 1;
|
|
|
|
int threadSize = std::floor((float) size / (float) nThreads);
|
|
|
|
int remaining = size - nThreads * threadSize;
|
|
|
|
std::vector<std::thread> threads;
|
|
|
|
for (int n = 0; n < nThreads; n++)
|
2023-01-24 06:26:24 +01:00
|
|
|
{
|
2023-01-29 16:27:15 +01:00
|
|
|
threads.emplace_back(runPerlinNoiseInThread, n * threadSize, threadSize, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderNoiseThread(nThreads * threadSize, remaining);
|
|
|
|
|
|
|
|
// Rejoin the threads
|
|
|
|
for (int t = 0; t < nThreads; ++t)
|
|
|
|
{
|
|
|
|
threads[t].join();
|
2023-01-24 06:26:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
});
|
2023-01-29 16:27:15 +01:00
|
|
|
|
|
|
|
// Duration of computation
|
|
|
|
auto stop = std::chrono::high_resolution_clock::now();
|
|
|
|
auto duration = std::chrono::duration_cast<std::chrono::seconds>(stop - start);
|
|
|
|
|
|
|
|
std::cout << "Finished computing Perlin noise for size " << size << " and grid size " << gridSize << " in "
|
|
|
|
<< duration.count() << " seconds" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PerlinNoise::runPerlinNoiseInThread(int xOffset, int xSize, PerlinNoise *noise)
|
|
|
|
{
|
|
|
|
noise->renderNoiseThread(xOffset, xSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PerlinNoise::renderNoiseThread(int xOffset, int xSize)
|
|
|
|
{
|
|
|
|
for (int x = xOffset; x < xOffset + xSize; x++)
|
|
|
|
{
|
|
|
|
for (int y = 0; y < size; y++)
|
|
|
|
{
|
|
|
|
for (int z = 0; z < size; z++)
|
|
|
|
{
|
|
|
|
setNoise(x, y, z, getGradientValue(x, y, z));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-24 06:26:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|