So I am trying to use 2D Perlin Noise for a Monogame project and I read through the wikipedia article (https://en.wikipedia.org/wiki/Perlin_noise) covering the approach and tried to code it. After a few failed attempts I copied some of the original C code written by Ken Perlin and translated it to C#. The results (which should gradually range from -1 to 1) all result near 0 through an entire cycle. The problem seems to be with the dot products too closely matching each other and giving near 0 results in the interpolate function, but I don't see any differences with the original implementation. I drew a 480x270 heightmap of the function output with a few different scales for x and y.
This is the attempt with x/48 y/27
while going from x = 0 -> 480 and y = 0 -> 270. When trying to divide by bigger numbers it mostly returns gray. When drawing the heightmap, I multiplied by 0.5f and added 0.5f to get the range from 0 to 1 for drawing black to white.
This is the code that I used:
public static float PerlinNoise(float x, float y)
{
//Get corner offsets
//0 is towards top left, 1 is towards bottom right
int x0 = (int)MathF.Floor(x);
int x1 = x0 + 1;
int y0 = (int)MathF.Floor(y);
int y1 = y0 + 1;
//Calculate dot products between gradient vector and distance vector
float[] dotProducts = new float[4];
dotProducts[0] = DotGridGradient(x0, y0, x, y);
dotProducts[1] = DotGridGradient(x1, y0, x, y);
dotProducts[2] = DotGridGradient(x0, y1, x, y);
dotProducts[3] = DotGridGradient(x1, y1, x, y);
//Interpolate the values
float sx = x - x0;
float sy = y - y0;
float interpolate1 = Interpolate(dotProducts[0], dotProducts[1], sx);
float interpolate2 = Interpolate(dotProducts[2], dotProducts[3], sx);
return Interpolate(interpolate1, interpolate2, sy);
}
private static float DotGridGradient(int ix, int iy, float x, float y)
{
// Seed the random number generator based on grid cell indices
int seed = 31 * ix + iy;
Random random = new Random(seed);
//---Confirmed working---
//Chooses a random angle between 0 and 2 pi (based on x and y to allow seeding)
float randomAngle = (float)random.NextDouble() * 2 * MathF.PI;
Vector2 gradientVector = new (MathF.Sin(randomAngle), MathF.Cos(randomAngle));
//Compute distance vector
float dx = x - ix;
float dy = y - iy;
return dx * gradientVector.X + dy * gradientVector.Y;
}
private static float Interpolate(float leftEdge, float rightEdge, float weight)
{
return (rightEdge - leftEdge) * weight + leftEdge;
}
After some trail and error I found the answer to my own problem, I'll post it here if anyone needs the solution at some point: The problem was in the random gradient vectors, this was my old solution:
I then tried the solution listed on the wikipedia page and that fixed the issue. The code (translated from C to C#) is: