Я пытаюсь реализовать Perlin Noise на C ++.
Во-первых, проблема (я думаю) в том, что результат не такой, как я ожидал. В настоящее время я просто использую сгенерированные значения шума Перлина в изображении в оттенках серого, и получаю следующие результаты:
Однако, насколько я понимаю, он должен выглядеть примерно так:
То есть шум, который я производю в настоящее время, больше похож на "стандартный" нерегулярный шум.
Это алгоритм шума Перлина, который я реализовал до сих пор:
float perlinNoise2D(float x, float y)
{
// Find grid cell coordinates
int x0 = (x > 0.0f ? static_cast<int>(x) : (static_cast<int>(x) - 1));
int x1 = x0 + 1;
int y0 = (y > 0.0f ? static_cast<int>(y) : (static_cast<int>(y) - 1));
int y1 = y0 + 1;
float s = calculateInfluence(x0, y0, x, y);
float t = calculateInfluence(x1, y0, x, y);
float u = calculateInfluence(x0, y1, x, y);
float v = calculateInfluence(x1, y1, x, y);
// Local position in the grid cell
float localPosX = 3 * ((x - (float)x0) * (x - (float)x0)) - 2 * ((x - (float)x0) * (x - (float)x0) * (x - (float)x0));
float localPosY = 3 * ((y - (float)y0) * (y - (float)y0)) - 2 * ((y - (float)y0) * (y - (float)y0) * (y - (float)y0));
float a = s + localPosX * (t - s);
float b = u + localPosX * (v - u);
return lerp(a, b, localPosY);
}
Функция calculateInfluence генерирует случайный вектор градиента и вектор расстояния для одной из угловых точек текущей ячейки сетки и возвращает их скалярное произведение. Реализован он как:
float calculateInfluence(int xGrid, int yGrid, float x, float y)
{
// Calculate gradient vector
float gradientXComponent = dist(rdEngine);
float gradientYComponent = dist(rdEngine);
// Normalize gradient vector
float magnitude = sqrt( pow(gradientXComponent, 2) + pow(gradientYComponent, 2) );
gradientXComponent = gradientXComponent / magnitude;
gradientYComponent = gradientYComponent / magnitude;
magnitude = sqrt(pow(gradientXComponent, 2) + pow(gradientYComponent, 2));
// Calculate distance vectors
float dx = x - (float)xGrid;
float dy = y - (float)yGrid;
// Compute dot product
return (dx * gradientXComponent + dy * gradientYComponent);
}
Здесь dist - генератор случайных чисел из C ++ 11:
std::mt19937 rdEngine(1);
std::normal_distribution<float> dist(0.0f, 1.0f);
А lerp просто реализован как:
float lerp(float v0, float v1, float t)
{
return ( 1.0f - t ) * v0 + t * v1;
}
Для реализации алгоритма я в первую очередь использовал следующие два ресурса:
Часто задаваемые вопросы по шуму Perlin Псевдокод шума Перлина
Мне сложно точно определить, где я, кажется, ошибаюсь. Возможно, я неправильно генерирую векторы градиента, так как я не совсем уверен, какой тип распределения они должны иметь. Я пробовал с равномерным распределением, но это, похоже, создавало повторяющиеся узоры в текстуре!
Точно так же может случиться так, что я неправильно усредняю значения влияния. Из статьи часто задаваемых вопросов о Perlin Noise было немного сложно понять, как именно это должно быть сделано.
Есть ли у кого-нибудь подсказки относительно того, что может быть не так с кодом? :)