Estoy buscando una manera agradable y fácil de generar una máscara para un mapa de isla con C #.
Básicamente estoy usando un mapa de altura aleatorio generado con ruido perlin, donde el terreno NO está rodeado de agua.
El siguiente paso sería generar una máscara, para garantizar que las esquinas y los bordes sean solo agua.
Entonces puedo restar la máscara de la imagen de ruido perlin para obtener una isla.
y jugando con el contraste ...
y la curva de gradiente, puedo obtener un mapa de altura de la isla tal como lo quiero ...
(estos son solo ejemplos, por supuesto)
como puede ver, los "bordes" de la isla simplemente están cortados, lo que no es un gran problema si el valor del color no es demasiado blanco, porque dividiré la escala de grises en 4 capas (agua, arena, hierba y rock).
Mi pregunta es, ¿cómo puedo generar una máscara atractiva como en la segunda imagen?
ACTUALIZAR
He encontrado esta técnica, parece ser un buen punto de partida para mí, pero no estoy seguro de qué tan bien puedo implementarla para obtener el resultado deseado. http://mrl.nyu.edu/~perlin/experiments/puff/
ACTUALIZACIÓN 2
Esta es mi solución final.
He implementado la makeMask()
función dentro de mi ciclo de normalización de esta manera:
//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 ] );
}
}
y esta es la función final:
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;
}
esto dará una salida como en la imagen # 3.
con un poco de cambio en el código, puede obtener el resultado deseado originalmente como en la imagen # 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;
}
}