Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Perlin Noise Trouble, wierd artifacts


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
5 replies to this topic

#1 Bryce²   Members   -  Reputation: 102

Like
0Likes
Like

Posted 17 December 2011 - 05:47 AM

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.
Posted Image


Posted Image




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 Posted Image.

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

Sponsor:

#2 bjorngylling   Members   -  Reputation: 100

Like
0Likes
Like

Posted 30 December 2011 - 04:31 AM

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.

#3 Olof Hedman   Crossbones+   -  Reputation: 2906

Like
0Likes
Like

Posted 30 December 2011 - 05:58 AM

Maybe just port the reference implementation and use that?

http://cs.nyu.edu/~perlin/noise/

(improved version, as published by Ken Perlin himself)

That is, if you're more interested in generating nice maps, and less interested in the details of the noise itself.

#4 Sirisian   Crossbones+   -  Reputation: 1771

Like
0Likes
Like

Posted 30 December 2011 - 08:58 AM

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.

#5 bjorngylling   Members   -  Reputation: 100

Like
0Likes
Like

Posted 02 January 2012 - 09:54 AM


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.


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.

#6 FLeBlanc   Crossbones+   -  Reputation: 3109

Like
0Likes
Like

Posted 02 January 2012 - 10:35 AM

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.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS