Я пытался написать собственную реализацию алгоритма шума Перлина на основе этой статьи. Однако в конечном итоге получается странный шаблон, который довольно далек от того результата, который я искал.
Вот мой код:
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'.");
}
}
И созданное изображение выглядит так:
[img]https://i.sstatic.net /trRr45ny.png[/img]
Я пробовал возиться с функцией шума и функцией скалярного произведения, но не уверен, что на самом деле происходит не так. Я подозреваю, что допустил глупую ошибку в логике, поэтому, если бы кто-нибудь из вас указал мне на это, я был бы очень признателен.
Я пытался написать собственную реализацию алгоритма шума Перлина на основе этой статьи. Однако в конечном итоге получается странный шаблон, который довольно далек от того результата, который я искал. Вот мой код: [code]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;
// 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'."); } } [/code] И созданное изображение выглядит так: [img]https://i.sstatic.net /trRr45ny.png[/img]
Я пробовал возиться с функцией шума и функцией скалярного произведения, но не уверен, что на самом деле происходит не так. Я подозреваю, что допустил глупую ошибку в логике, поэтому, если бы кто-нибудь из вас указал мне на это, я был бы очень признателен.