5
\$\begingroup\$

I've made a Heightmap Generator that creates island heightmaps like in this picture:

enter image description here

I am dividing the grayscale ( 0 - 255 ) into 4 diferent parts ( water, sand, gras and forrest ) and after coloring those layers the heightmap looks like this:

enter image description here

As you can see, the "coast" is pretty noisy ( there are some sand-pixels on the edges, that are not connected to the island itself )

So my question is, How to smooth that noisy coast / beach ?

Is there any quick method that detects those "lonly" pixels and turns them into water ?

can i use the smoothing function below to smooth the final output ?

( I'm working with either a float[][] or a Color[][] array )


UPDATE

here is my smoothing function that i've implemented in my heightmap generation core.

i don't know if i can use this function for this purpose.

    public static float[][] GenerateSmoothNoise( float[][] baseNoise, int octave ) {
        int width = baseNoise.Length;
        int height = baseNoise[ 0 ].Length;

        float[][] smoothNoise = GetEmptyArray<float>( width, height );

        int samplePeriod = 1 << octave; // calculates 2 ^ k
        float sampleFrequency = 1.0f / samplePeriod;

        for( int i = 0; i < width; i++ ) {
            //calculate the horizontal sampling indices
            int sample_i0 = ( i / samplePeriod ) * samplePeriod;
            int sample_i1 = ( sample_i0 + samplePeriod ) % width; //wrap around
            float horizontal_blend = ( i - sample_i0 ) * sampleFrequency;

            for( int j = 0; j < height; j++ ) {
                //calculate the vertical sampling indices
                int sample_j0 = ( j / samplePeriod ) * samplePeriod;
                int sample_j1 = ( sample_j0 + samplePeriod ) % height; //wrap around
                float vertical_blend = ( j - sample_j0 ) * sampleFrequency;

                //blend the top two corners
                float top = Interpolate( baseNoise[ sample_i0 ][ sample_j0 ],
                    baseNoise[ sample_i1 ][ sample_j0 ], horizontal_blend );

                //blend the bottom two corners
                float bottom = Interpolate( baseNoise[ sample_i0 ][ sample_j1 ],
                    baseNoise[ sample_i1 ][ sample_j1 ], horizontal_blend );

                //final blend
                smoothNoise[ i ][ j ] = Interpolate( top, bottom, vertical_blend );
            }
        }

        return smoothNoise;
    }

and for those who are interested, i'm calling GenerateSmoothNoise() in this way ->

    public static float[][] GeneratePerlinNoise( float[][] baseNoise, int octaveCount ) {
        int width = baseNoise.Length;
        int height = baseNoise[ 0 ].Length;

        float[][][] smoothNoise = new float[ octaveCount ][][]; //an array of 2D arrays containing

        float persistance = 0.7f;

        //generate smooth noise
        for( int i = 0; i < octaveCount; i++ ) {
            smoothNoise[ i ] = GenerateSmoothNoise( baseNoise, i );
        }

        float[][] perlinNoise = GetEmptyArray<float>( width, height ); //an array of floats initialised to 0

        float amplitude = 1.0f;
        float totalAmplitude = 0.0f;

        //blend noise together
        for( int octave = octaveCount - 1; octave >= 0; octave-- ) {
            amplitude *= persistance;
            totalAmplitude += amplitude;

            for( int i = 0; i < width; i++ ) {
                for( int j = 0; j < height; j++ ) {
                    perlinNoise[ i ][ j ] += smoothNoise[ octave ][ i ][ j ] * amplitude;
                }
            }
        }

        //normalisation
        for( int i = 0; i < width; i++ ) {
            for( int j = 0; j < height; j++ ) {
                perlinNoise[ i ][ j ] /= totalAmplitude;
            }
        }

        return perlinNoise;
    }
\$\endgroup\$
2
  • \$\begingroup\$ How about applying a gaussian, or another low-pass filter? it should remove those high-frequency noise elements. \$\endgroup\$ Apr 23, 2013 at 7:16
  • \$\begingroup\$ simple idea, i had it too. i thought that bluring the image will give me a not nice looking output, but i'll give it a try \$\endgroup\$
    – Ace
    Apr 23, 2013 at 12:21

2 Answers 2

3
\$\begingroup\$

Note: I'm gonna use the term "pixel" here to refer to a unit of land in your height map.

I'll assume your definition of noise are groups of land pixel are that smaller than certain size and you want these removed (turned into water).

In that case, a simple method may be just counting the total number of connected pixels (size) for each group of land pixels(islands) and removing ones with pixel count below a certain threshold. You can do this in O(n) time using flood fill (http://en.wikipedia.org/wiki/Flood_fill) as follows:

  1. For each pixel, start a flood-fill to count number connected land pixels (don't replace any pixels yet). Store this starting pixel in a list if number pixels < threshold. You flag each pixel that you've encountered in the algorithm so that you never look at the same pixel twice.
  2. For each pixel you stored in that list, flood-fill and replace land pixel with water pixel.

Pesudocode:

for each pixel at point p in map
    count = floodFillCount(p, map)
    if(count < threshold)
        remove_list.add(p)
for each point p in remove_list
    floodFillReplace(p, water_height, map)

Note that this method will not remove things like a very long thin lines of land.

\$\endgroup\$
3
  • \$\begingroup\$ thx for that solution. i also thought of usinf a flood algorythm, but i thought of smoothing the map after the noisemap generating and befor the layer assignment. there are smoe smooth functions like in this example ( scroll down to Smoothed Noise ) -> freespace.virgin.net/hugo.elias/models/m_perlin.htm i understand the smoothing function, and i also have implemented it in my core heightmap generating algorythm ( perlin noise ), but i can't figure out how i can use this function afterwards.. look at my update abow for the code. \$\endgroup\$
    – Ace
    Apr 22, 2013 at 23:11
  • \$\begingroup\$ Hmm.. I don't know anything about perlin noise. I'm kinda curious how that stuff works though so I might take a look at it later though. Hopefully someone knows that stuff well already and give a better answer. \$\endgroup\$ Apr 22, 2013 at 23:28
  • \$\begingroup\$ yeah thats really interesting stuff, i'm also just new to perlin noise heightmap generating. but i'll try your solution anyway ^^ \$\endgroup\$
    – Ace
    Apr 22, 2013 at 23:32
0
\$\begingroup\$

If someone wants to use the function GenerateSmoothNoise() in Unity for example here is a small adaptation (with the function Interpolate()) :

public static float[,] GenerateSmoothNoise(float[,] baseNoise, int octave, float frequency) {
    int width = baseNoise.GetLength(0);
    int height = width;

    float[,] smoothNoise = new float[width, height];

    int samplePeriod = 1 << octave; // calculates 2 ^ k

    for (int i = 0; i < width; i++) {
        //calculate the horizontal sampling indices
        int sample_i0 = (i / samplePeriod) * samplePeriod;
        int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
        float horizontal_blend = (i - sample_i0) * frequency;

        for (int j = 0; j < height; j++) {
            //calculate the vertical sampling indices
            int sample_j0 = (j / samplePeriod) * samplePeriod;
            int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
            float vertical_blend = (j - sample_j0) * frequency;

            //blend the top two corners
            float top = Interpolate(baseNoise[sample_i0, sample_j0],
                baseNoise[sample_i1, sample_j0], horizontal_blend);

            //blend the bottom two corners
            float bottom = Interpolate(baseNoise[sample_i0, sample_j1],
                baseNoise[sample_i1, sample_j1], horizontal_blend);

            //final blend
            smoothNoise[i, j] = Interpolate(top, bottom, vertical_blend);
        }
    }
    return smoothNoise;
}

public static float Interpolate(float a, float b, float x) {
    float ft = x * 3.1415927f;
    float f = (float)(1 - Math.Cos(ft)) * 0.5f;
    return a * (1 - f) + b * f;
}

And it's possible to create a very cool effect like in Minecraft by using, for example, octaves = 4 and frequency = 10f.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .