• Advertisement
Sign in to follow this  

Perlin Noise Trouble, wierd artifacts

This topic is 2246 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey GameDev,

I am doing a school project and i am trying to make a Perlin Noise generated game map that is created each game at startup. I am doing the project in XNA 4.0 and have had no trouble programming in C# until now.
They teach pasal/Delphi at my school but I have started using c# after my teacher told me it was a better choice.

Here are some screenshots and the code i am using to make the perlin noise array.

I am using a 16x16 array of floating point variables to calculate the types of land on the map. For my testing i am using only grass, water and sand.

Screenshots: All of the results typically look like this.
perlinnoise.jpg


perlinnoise2.jpg




Here is the code i am using: (I know its messy. I tried to keep it clean but after an hour or so of tinkering with the maths it has become a nightmare)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace RandomMapLib.MapGeneration
{
public static class PerlinNoise
{
#region fields
static float[,] map; // 2d array with multiple octaves - i.e maps[octave][dimension1, dimension2]
static int seed;
static int seedCap = 10000;
#endregion

#region properties
public static int Seed
{
get { return seed; }
set { seed = (int)MathHelper.Clamp(value, 0, seedCap); }
}

public static int SeedCap
{
get { return seedCap; }
set { seedCap = value; }
}
#endregion

#region methods

/// <summary>
/// Generate a new random seed for the perlin noise generator
/// </summary>
public static void newSeed()
{
Random newSeed = new Random();
seed = newSeed.Next(0, seedCap);

}


/// <summary>
/// Generates a random value between 0.0 and 1.0 to 4d.p for the point specified using the seed
/// for this static class;
/// </summary>
/// <param name="x">the point on the x axis</param>
/// <param name="y">the point on the y axis</param>
/// <returns>the value generated with the seed</returns>
public static float noise(float x, float y)
{
float n;
Random random = new Random();

n = (float)random.Next(0,99)/100;

return n;
}


/// <summary>
/// Uses this classes noise function and applies simple smoothing to it
/// </summary>
/// <param name="x">the point on the x axis</param>
/// <param name="y">the point on the y axis</param>
/// <returns>the smooth noise for the point given in the [x, y] param</returns>
public static float smoothNoise(float x, float y)
{
float corners;
float sides;
float centre;

corners = (noise(x - 1, y - 1) + noise(x - 1, y + 2) + noise(x + 1, y - 1) + noise(x + 1, y + 1)) / 16;
sides = (noise(x - 1, y) + noise(x, y - 1) + noise(x + 1, y) + noise(x, y + 1)) / 8;
centre = noise(x, y) / 4;

return corners + sides + centre;
}

/// <summary>
/// Returns an interpolated smooth noise value
/// </summary>
/// <param name="x">x coord</param>
/// <param name="y">y coord</param>
/// <returns>smooth noise value</returns>
public static float interpolatedNoise(float x, float y)
{
int intX = (int)x;
float fracX = (float)x - intX;

int intY = (int)y;
float fracY = (float)y - intY;

float v1, v2, v3, v4, i1, i2;

v1 = smoothNoise(intX, intY);
v2 = smoothNoise(intX + 1, intY);
v3 = smoothNoise(intX, intY + 1);
v4 = smoothNoise(intX + 1, intY + 1);

i1 = MathHelper.SmoothStep(v1, v2, fracX);
i2 = MathHelper.SmoothStep(v3, v4, fracY);

return MathHelper.SmoothStep(i1, i2, fracY);
}

/// <summary>
/// Gets a blended perlin point for the specified x,y coordinate for the number of octaves shown;
/// </summary>
/// <param name="x">the x coordinate</param>
/// <param name="y">the y coordinate</param>
/// <param name="persistance"> ||0 to 1|| --- the amplitude multiplier for each frequency</param>
/// <param name="octaves">the number of frequencies being blended</param>
/// <returns>a single point that is blended across all octaves</returns>
public static float perlinNoise(float x, float y, float persistance, int octaves)
{
float total = 0;
float p = persistance;
float frequency;
float amplitude;
int n = octaves - 1;

for (int i = 0; i < n; i++)
{
frequency = (float)Math.Pow(2, i);
amplitude = (float)Math.Pow(p, i);

total += noise(x * frequency, y * frequency) * amplitude;
}

return total;
}




#endregion
}
}



and my world object which uses the perlinNoise static class... --- It should be noted that the world object was working perfectly when i was using random noise to populate the map. so thats not really going to be broken, even though the code looks shocking. The TileManager class is pretty solid and i have had no problems with it.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;

using RandomMapLib;
using RandomMapLib.Tiles;
using RandomMapLib.MapGeneration;

namespace RandomMapTest
{
public class World : TileManager
{
float[,] map;

public World(Game game, GraphicsDeviceManager gdm)
: base(game, gdm)
{
map = new float[64, 64];

}

protected override void LoadContent()
{
base.LoadContent();

int ArraySize = map.GetLength(0);
Random rnd = new Random();
int type;

for (int x = 0; x < ArraySize; x++)
{
for (int y = 0; y < ArraySize; y++)
{
map[x, y] = PerlinNoise.perlinNoise((float)x, (float)y, 0.5f, 3);
}
}

spriteBatch = new SpriteBatch(GraphicsDevice);

ContentManager Content = Game.Content;

Texture2D tex = Content.Load<Texture2D>(@"map\mapTiles");

TileArray = new Tile[ArraySize, ArraySize];

for (int y = 0; y < ArraySize; y++)
{
for (int x = 0; x < ArraySize; x++)
{

map[x, y] = PerlinNoise.perlinNoise((float)x, (float)y, 0.5f, 3);
if (map[x, y] < 0.5f)
type = (int)TileType.Water;
//else if (map[x, y] < 0.5)
// type = (int)TileType.Sand;
else
type = (int)TileType.Grass;
TileArray[x, y] = new Tile(tex, (TileType)type);
TileArray[x, y].Position = new Point((x) * Tile.TileRes, (y) * Tile.TileRes);
}
}
}

public void generateMap()
{


int ArraySize = map.GetLength(0);
Random rnd = new Random();
int type;

for (int x = 0; x < ArraySize; x++)
{
for (int y = 0; y < ArraySize; y++)
{
map[x, y] = PerlinNoise.noise((float)x, (float)y);
}
}

spriteBatch = new SpriteBatch(GraphicsDevice);

ContentManager Content = Game.Content;

Texture2D tex = Content.Load<Texture2D>(@"map\mapTiles");

TileArray = new Tile[ArraySize, ArraySize];

for (int y = 0; y < ArraySize; y++)
{
for (int x = 0; x < ArraySize; x++)
{
if (map[x, y] < 0.3f)
type = (int)TileType.Water;
else if (map[x, y] < 0.5)
type = (int)TileType.Sand;
else
type = (int)TileType.Grass;
TileArray[x, y] = new Tile(tex, (TileType)type);
TileArray[x, y].Position = new Point((x) * Tile.TileRes, (y) * Tile.TileRes);
}
}

}

public override void Update(GameTime gameTime)
{
base.Update(gameTime);

KeyboardState kb = Keyboard.GetState();

if (kb.IsKeyDown(Keys.Enter))
{
PerlinNoise.newSeed();
generateMap();
}
}

public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
{
base.Draw(gameTime);
}
}

}


If none of the above makes much sense its because i am really rushing through this to get to bed for much needed sleep mellow.gif.

Thanks for any feedback or even reading this shambles of a post,

Bryce

p.s i tried to upload the project but it exceeds 50mb so i thought there would be no point

Share this post


Link to post
Share on other sites
Advertisement
I'm no expert on this subject but I recently used Perlin Noise to generate a tilemap.

You have a few problems actually, your perlinNoise function seems to be calling the function noise in the loop. It should probably be calling interpolatedNoise? Also your noise function to get a (not-so)-random number is actually returning a random number since it creates a new Random instance every time without taking the seed you already have set up into account.

I also found it helpful to print the actual noise to screen. Convert the value from the scale 0-1 to a scale of 0-255 and then print each pixel as a greyscale (you could even do this with winforms instead of XNA). This helped me a lot to get it running correctly and set up the different parameters to get a good looking map.

The article http://freespace.virgin.net/hugo.elias/models/m_perlin.htm helped me a lot to understand what Perlin Noise actually is and the theory behind how it should work as well as some pretty good pointers to how it can be implemented. I suggest you have a look at that.

Share this post


Link to post
Share on other sites

The article http://freespace.vir...ls/m_perlin.htm helped me a lot to understand what Perlin Noise actually is and the theory behind how it should work as well as some pretty good pointers to how it can be implemented. I suggest you have a look at that.

This comes up a lot. That tutorial is actually value noise, not gradient noise (like perlin). Looks like the OP used that tutorial. It's good to realize though that perlin noise is implemented differently than value noise.

Share this post


Link to post
Share on other sites

[quote name='bjorngylling' timestamp='1325241094' post='4898096']
The article http://freespace.vir...ls/m_perlin.htm helped me a lot to understand what Perlin Noise actually is and the theory behind how it should work as well as some pretty good pointers to how it can be implemented. I suggest you have a look at that.

This comes up a lot. That tutorial is actually value noise, not gradient noise (like perlin). Looks like the OP used that tutorial. It's good to realize though that perlin noise is implemented differently than value noise.
[/quote]

Oh! For some reason I got that completely mixed up then. Am I correct in thinking the main difference is that Perlin Noise has a list of random numbers generated once which the 'random' function picks from and value noise 'calculates' the 'random' from a function (or a Random class with the same seed?)? Or is the difference bigger than that? I find the reference implementation quite hard to understand, maybe I should give it another go some day.

Share this post


Link to post
Share on other sites
Original Perlin noise is implemented such that the pseudo-random value for a lattice point is calculated as a wave function. Each lattice point is assigned a randomized vector in N-dimensional space (N, of course, being the dimensionality of the noise function) which defines the gradient direction of the wavelet function at that point. A wavelet function basically describes a spherical region around the point. One "side" of the sphere (indicated by the gradient vector) evaluates positive, and the opposite side evaluates negative. This wavelet function is best described by looking at how dot-product works.

The final value for a point is calculated by interpolating the results you get from the wavelet functions of all 4 (or 8 or 16, etc...) enclosing lattice points.

It's all a bit more involved than simply generating a random value per lattice point, and has the benefit of moving the peaks and valleys of the function away from the lattice edges, thus reducing the occurrence of lattice-based artifacts.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement