我一直在尝试根据这篇论文编写自己的 Perlin 噪声算法实现。然而,它最终产生了一个奇怪的模式,这与我正在寻找的结果相去甚远。
这是我的代码:
using System;
using System.Drawing;
class PerlinNoise2D
{
// Fade function for smoothing transitions
private static double Fade(double t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
// Linear interpolation function
private static double Lerp(double t, double a, double b)
{
return a + t * (b - a);
}
// Dot product of gradient and displacement vectors
private static double DotGridGradient(int gridX, int gridY, double x, double y, Random rand)
{
// Generate a pseudo-random gradient vector at the grid point
double angle = rand.NextDouble() * Math.PI * 2;
double gradX = Math.Cos(angle);
double gradY = Math.Sin(angle);
// Displacement vector from grid point to input point
double dx = x - gridX;
double dy = y - gridY;
// Return dot product
return (dx * gradX + dy * gradY);
}
// Noise function for a single layer
public static double Noise(double x, double y, int gridSize, Random rand)
{
// Identify the grid cell the point is in
int x0 = (int)Math.Floor(x) % gridSize;
int y0 = (int)Math.Floor(y) % gridSize;
int x1 = (x0 + 1) % gridSize;
int y1 = (y0 + 1) % gridSize;
// Local coordinates within the grid cell
double localX = x - Math.Floor(x);
double localY = y - Math.Floor(y);
// Apply fade function to smooth transitions
double xFade = Fade(localX);
double yFade = Fade(localY);
// Compute dot products with gradients at each corner
double n00 = DotGridGradient(x0, y0, x, y, rand);
double n10 = DotGridGradient(x1, y0, x, y, rand);
double n01 = DotGridGradient(x0, y1, x, y, rand);
double n11 = DotGridGradient(x1, y1, x, y, rand);
// Interpolate along x for the two rows
double nx0 = Lerp(xFade, n00, n10);
double nx1 = Lerp(xFade, n01, n11);
// Interpolate along y for the final noise value
return Lerp(yFade, nx0, nx1);
}
// Perlin noise with multiple octaves for fractal-like detail
public static double Perlin(double x, double y, int gridSize, int octaves, double persistence)
{
double total = 0;
double frequency = 1;
double amplitude = 1;
double maxValue = 0;
// Random seed for consistent results (same noise pattern for the same inputs)
Random rand = new(69);
// Iterates through octaves (layers of noise)
for (int i = 0; i < octaves; i++)
{
// Generates noise for current octave (scaled by frequency and amplitude)
total += Noise(x * frequency, y * frequency, gridSize, rand) * amplitude;
// Amplitude added to max possible value
maxValue += amplitude;
// Persistence is a factor between 0 and 1, which dictates how much each successive octave contributes
amplitude *= persistence;
// Frequency is doubled as higher octaves have smaller frequencies
frequency *= 2;
}
// Returns noise normalized to a [0, 1] range
return total / maxValue;
}
static void Main()
{
// Configuration for the Perlin noise
int width = 512; // Width of the output image
int height = 512; // Height of the output image
int gridSize = 16; // Size of the grid for noise
int octaves = 4; // Number of noise octaves
double persistence = 0.5; // Persistence factor
// Create a bitmap to store the Perlin noise
Bitmap bitmap = new Bitmap(width, height);
// Generate Perlin noise for each pixel
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// Normalize coordinates for Perlin noise
double noiseX = (double)x / width * gridSize;
double noiseY = (double)y / height * gridSize;
// Compute Perlin noise value using the provided implementation
double noiseValue = Perlin(noiseX, noiseY, gridSize, octaves, persistence);
// Map noise value to grayscale (0-255)
int gray = (int)(noiseValue * 255);
gray = Math.Max(0, Math.Min(255, gray)); // Ensure it's within valid range
Color color = Color.FromArgb(gray, gray, gray);
// Set the pixel color in the bitmap
bitmap.SetPixel(x, y, color);
}
}
// Save the image to a file
bitmap.Save("PerlinNoise.png");
Console.WriteLine("Perlin noise image saved as 'PerlinNoise.png'.");
}
}
创建的图像如下所示:
我尝试摆弄噪声函数和点积函数,但我不确定到底出了什么问题。我怀疑我在其背后的逻辑上犯了一个愚蠢的错误,所以如果你们中有人能为我指出这一点,那么我将不胜感激。
您的
DotGridGradient
应始终为相同的网格坐标生成相同的随机数。据我所知,事实并非如此,因为它是使用相同的种子为每个像素重新创建的,这可能解释了您所看到的重复模式。
最简单的解决方案可能是预先生成梯度网格。另一种可能的解决方案是使用网格坐标作为生成器的种子。我从未编写过 perlin 生成器,所以我不确定首选什么解决方案。
其他一些建议