cloudy-raytracer/common/noise/perlinnoise.cpp

150 lines
4.4 KiB
C++
Raw Normal View History

2023-01-24 06:26:24 +01:00
#include <random>
#include <algorithm>
#include <thread>
#include <iostream>
#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()
{
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
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
{
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);
});
// 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);
}