# A Simple Method To Create Island Map Mask

I'm searching for a nice and easy way to generate a mask for an island map with C#.

Basically I'm using with a random heightmap generated with perlin noise, where the terrain is NOT surrounded by water.

The next step would be to generate a mask, to ensure the corners and borders are just water.

Then I can just subtract the mask from the perlin noise image to get an island.

and playing around with the contrast..

and the gradient curve, I can get an island heightmap just like I want it..

(these are just examples of course)

so as you can see, the "edges" of the island are just cut off, which is not a big problem if the colour value is not too white, because I will just divide the grayscale into 4 layers (water, sand, grass and rock).

My question is, how can I generate a good looking mask like in the second image?

UPDATE

I've found this technique, it seems to be a nice starting point for me, but I'm not sure how exectly I can implement it to get the desired output. http://mrl.nyu.edu/~perlin/experiments/puff/

UPDATE 2

this is my final solution.

I've implemented the makeMask() function inside my normalisation loop like this:

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


and this is the final function:

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
int maxVal = ( ( ( height + width ) / 2 ) / 100 * 10 );
if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
return 0;
} else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
return oldValue;
} else {
float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
return oldValue * factor;
}
}

private static float getFactor( int val, int min, int max ) {
int full = max - min;
int part = val - min;
float factor = (float)part / (float)full;
return factor;
}

public static int getDistanceToEdge( int x, int y, int width, int height ) {
int[] distances = new int[]{ y, x, ( width - x ), ( height - y ) };
int min = distances[ 0 ];
foreach( var val in distances ) {
if( val < min ) {
min = val;
}
}
return min;
}


this will give an output like in the image #3.

with a little bit of change in the code, you can get the originally wanted output like in image #2 ->

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
int maxVal = ( ( ( height + width ) / 2 ) / 100 * 20 );
if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
return 0;
} else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
return 1;
} else {
float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
return ( oldValue + oldValue ) * factor;
}
}

• any chance you can link to youur full source? – stoic May 17 '17 at 15:24

Generate regular noise with a bias for higher values towards the center. If you're wanting square-ish island shapes like you show in your example, I would use the distance to the closest edge as your factor.

With that factor, you can use something like the following when generating the mask noise:

maxDVal = (minIslandSolid - maxIslandSolid)

d = distanceToNearestEdge(x,y)
if(d < maxIslandSolid)
return isSolid(NonSolidValue) //always non-solid for outside edges
else if(d < minIslandSolid)
//noisy edges
return isSolid((1 - (d/maxDVal)) * noiseAt(x,y))
else
return isSolid(SolidValue) //always return solid for center of island


Where distanceToNearestEdge returns the distance to the nearest edge of the map from that position. And isSolid decides if a value between 0 and 1 is solid (whatever your cut off is). It's a very simple function, it might look like this:

isSolid(float value) return value < solidCutOffValue

Where solidCutOffValue is whatever value you're using to decide between solid or not. It could be .5 for even split, or .75 for more solid or .25 for less solid.

Finally, this little bit (1 - (d/maxDVal)) * noiseAt(x,y). First, we get a factor between 0 and 1 with this:

(1 - (d/maxDVal))

Where 0 is on the outside edge and 1 is on the inside edge. This means our noise is more likely to be solid at the inside and non-solid at the outside. This is the factor we apply to the noise we get from noiseAt(x,y).

Here is a more visual representation of what the values are, as the names might be misleading to the actual values:

• thx for that quick answer, i'll try to implement this technique. hope to get the desired output with this. – Ace Apr 22 '13 at 17:05
• It'll likely take some tweaking to get the noisy edges the way you want them. But this should be a solid basis for you. Good luck! – MichaelHouse Apr 22 '13 at 17:12
• so far i got the base implementation to work, can you just give me an example of the IsSolid function ? i dont know how i can get a value between 0 and 1 based on the min and max distance from the edge. see my update for my code so far. – Ace Apr 22 '13 at 18:12
• I had some mixed up logic in there. I've fixed it to make more sense. And provided an example of isSolid – MichaelHouse Apr 22 '13 at 18:36
• To get a value between 0 and 1, you just find out what the maximum value can be and divide your current value by that. Then I subtracted that from one, because I wanted zero to be on the outside edge and 1 to be on the inside edge. – MichaelHouse Apr 22 '13 at 18:47

If you're willing to spare some computational power for this, then you could use a similar technique to what the author of this blog did. (NB: If you wish to directly copy his code, it's in ActionScript). Basically, he generates quasi-random points (i.e. looks relatively uniform) and then uses these to create Voronoi polygons.

He then sets the outside polygons to water and iterates through the rest of the polygons, making them water if a certain percentage of the adjacent polygons are water. You're then left with a polygon mask roughly representing an island.

From this you can apply noise to the edges, resulting in something resembling this (the colours are from another, unrelated step):

You are then left with a (quite) realistic-looking island-shaped mask, which would serve your purposes. You could choose to use it as a mask for your Perlin noise, or you could then generate height values based on distance to the sea and add noise (although that seems unnecessary).

• thx for your reply, but this woth the first ( an pretty much only ) solution i got from searching the web. this solution seemy to be very nice, but i would like to try the "simple" way. – Ace Apr 22 '13 at 18:58
• @Ace Fair enough, it probably is a bit overkill for whatever you're going to do :P Still, it's worth keeping in mind should you ever need it. – Polar Apr 22 '13 at 20:08
• Glad someone linked to this - that page is always on my list of "really fantastic posts about how someone implemented something." – Tim Holt Apr 22 '13 at 21:52
• +1. This is so cool. Thank you for this, it'll definitely be helpful for me! – Andre Apr 22 '13 at 22:59

One very simple method is to create inverse radial or spherical gradient with center at the width / 2 and height /2. For masking you want to subtract the gradient from the noise rather than multiply it. This gives you more realistic looking shores with the drawback that the islands are not necessarily connected.

You can see the difference between subtracting and multiplying the noise with the gradient here: http://www.vfxpedia.com/index.php?title=Tips_and_Techniques/Natural_Phenomena/Smoke

If you are unsure how to create radial gradient, you can use this as a start point:

    public static float[] CreateInverseRadialGradient(int size, float heightScale = 1)
{
float radius = size / 2;

float[] heightMap = new float[size * size];

for (int iy = 0; iy < size; iy++)
{
int stride = iy * size;
for (int ix = 0; ix < size; ix++)
{
float centerToX = ix - radius;
float centerToY = iy - radius;

float distanceToCenter = (float)Math.Sqrt(centerToX * centerToX + centerToY * centerToY);
heightMap[iy * size + ix] = distanceToCenter / radius * heightScale;
}
}

return heightMap;
}


Don't forget to scale your gradient to the same heightas your height map and you still need to factor in your water line somehow.

The problem with this method is that your height field will be centered around the center of the map. This method, however, should get you started with adding features and making your landscape more diverse as you can use addition to add features to your height map.

• thx for the reply. i'm not sure if i understand you right, but did you notice that the mask doesnt affect the original heightmap data at all, it is just affected in the negative, so it just defines the pixes that are displayed ( in% ) or not. but also i've tryed it with simple gradiants, and i was not happy with the result. – Ace Apr 22 '13 at 20:10

I second ollipekka's suggestion: what you want to do is subtract a suitable bias function from your heightmap, so that the edges are guaranteed to be underwater.

There are plenty of suitable bias functions, but one fairly simple one is:

f(x, y) = 1 / (x * (1-x) * y * (1-y)) - 16


where x and y are the coordinate values, scaled to lie between 0 and 1. This function takes the value 0 at the center of the map (at x = y = 0.5) and tends to infinity at the edges. Thus, subtracting it (scaled by a suitable constant factor) from your height map ensures that the height values will also tend to minus infinity near the map edges. The just pick any arbitrary height you want and call it the sea level.

As ollipekka notes, this approach will not guarantee that the island will be contiguous. However, scaling the bias function by a fairly small scale factor should make it mostly flat in the middle area of the map (thus not affecting your terrain much), with a significant bias appearing only near the edges. Thus, doing so should give you a squarish, mostly contiguous island with, at most, possible a few tiny sub-islands near the edges.

Of course, if you don't mind the possibility of disconnected terrain, a somewhat larger scaling factor should give you more water and a more natural-looking island shape. Adjusting the sea level and/or the scale of your original heightmap can also be used to vary the size and shape of the resulting island(s).