Я ищу хороший и простой способ создать маску для карты острова с помощью C #.
В основном я использую случайную карту высот, сгенерированную с помощью перлин-шума, где ландшафт НЕ окружен водой.
Следующим шагом будет создание маски, чтобы углы и границы были просто водой.
Тогда я могу просто вычесть маску из изображения перлин-шума, чтобы получить остров.
и играть с контрастом ..
и кривая градиента, я могу получить карту высот острова так, как я этого хочу ..
(это всего лишь примеры, конечно)
так что, как вы можете видеть, «края» острова просто обрезаются, что не является большой проблемой, если значение цвета не слишком белое, потому что я просто разделю оттенки серого на 4 слоя (вода, песок, трава и камень).
Мой вопрос, как я могу создать красивую маску, как на втором изображении?
ОБНОВИТЬ
Я нашел эту технику, она мне кажется хорошей отправной точкой, но я не уверен, насколько точно я могу ее реализовать, чтобы получить желаемый результат. http://mrl.nyu.edu/~perlin/experiments/puff/
ОБНОВЛЕНИЕ 2
это мое окончательное решение.
Я реализовал makeMask()
функцию внутри цикла нормализации следующим образом:
//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 ] );
}
}
и это последняя функция:
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;
}
это даст вывод, как на картинке # 3.
с небольшими изменениями в коде вы можете получить исходный искомый вывод, как на изображении №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;
}
}
Ответы:
Создайте регулярный шум с уклоном для более высоких значений к центру. Если вы хотите иметь форму островков прямоугольной формы, как показано в примере, я бы использовал расстояние до ближайшего края в качестве вашего фактора.
С этим фактором вы можете использовать что-то вроде следующего при генерации шума маски:
Где
distanceToNearestEdge
возвращает расстояние до ближайшего края карты от этой позиции. ИisSolid
решает, является ли значение между 0 и 1 сплошным (независимо от того, что вы обрезаете). Это очень простая функция, она может выглядеть так:isSolid (значение с плавающей точкой) возвращаемое значение <solidCutOffValue
Где
solidCutOffValue
какое значение вы используете, чтобы выбирать между твердым или нет. Это может быть.5
даже для разделения, или.75
для более твердого или.25
для менее твердого.Наконец, это немного
(1 - (d/maxDVal)) * noiseAt(x,y)
. Во-первых, мы получаем коэффициент от 0 до 1 с этим:(1 - (d/maxDVal))
Где
0
на внешнем краю и1
на внутреннем краю. Это означает, что наш шум с большей вероятностью будет сплошным внутри и не сплошным снаружи. Это фактор, который мы применяем к шуму, который мы получаемnoiseAt(x,y)
.Вот более наглядное представление значений, поскольку имена могут вводить в заблуждение фактические значения:
источник
isSolid
Если вы готовы сэкономить некоторые вычислительные мощности для этого, то вы можете использовать метод, аналогичный тому, что делал автор этого блога . ( Примечание: если вы хотите напрямую скопировать его код, он написан на ActionScript). По сути, он генерирует квазислучайные точки (т.е. выглядит относительно равномерно), а затем использует их для создания многоугольников Вороного .
Затем он устанавливает внешние полигоны на воду и перебирает остальные полигоны, превращая их в воду, если определенный процент смежных полигонов - вода . Затем вы остаетесь с многоугольной маской, грубо представляющей остров.
Из этого вы можете применить шум к краям, что приведет к чему-то похожему на это (цвета из другого, не связанного с этим шага):
Затем вы получаете (вполне) реалистично выглядящую маску в форме острова, которая будет служить вашим целям. Вы можете использовать его в качестве маски для шума Перлина, или затем вы можете генерировать значения высоты на основе расстояния до моря и добавлять шум (хотя это кажется ненужным).
источник
Один очень простой метод - создать обратный радиальный или сферический градиент с центром в ширину / 2 и высоту / 2. Для маскировки вы хотите вычесть градиент из шума, а не умножить его. Это дает вам более реалистично выглядящие берега с тем недостатком, что острова не обязательно связаны между собой.
Вы можете увидеть разницу между вычитанием и умножением шума с градиентом здесь: http://www.vfxpedia.com/index.php?title=Tips_and_Techniques/Natural_Phenomena/Smoke
Если вы не знаете, как создать радиальный градиент, вы можете использовать это как отправную точку:
Не забудьте масштабировать ваш градиент до той же высоты, что и ваша карта высот, и вам все равно нужно как-то учесть ватерлинию.
Проблема с этим методом в том, что ваше поле высоты будет центрировано вокруг центра карты. Этот метод, однако, должен помочь вам начать добавлять объекты и сделать ландшафт более разнообразным, так как вы можете использовать дополнение для добавления объектов на карту высот.
источник
Второе предложение ollipekka: вы хотите вычесть подходящую функцию смещения из вашей карты высот, чтобы края гарантированно находились под водой.
Есть много подходящих функций смещения, но одна довольно простая:
где x и y - значения координат, масштабированные так, чтобы они лежали в диапазоне от 0 до 1. Эта функция принимает значение 0 в центре карты (при x = y = 0,5) и стремится к бесконечности по краям. Таким образом, вычитая его (масштабируется с помощью подходящего постоянного коэффициента) из вашей карты высот, вы убедитесь, что значения высоты также будут стремиться к минус бесконечность вблизи краев карты. Просто выберите любую произвольную высоту и назовите ее уровнем моря.
Как отмечает ollipekka, такой подход не гарантирует, что остров будет смежным. Однако масштабирование функции смещения с помощью довольно небольшого масштабного коэффициента должно сделать ее в основном плоской в средней области карты (таким образом, не сильно затрагивая вашу местность), при этом значительный уклон появляется только вблизи краев. Таким образом, в результате вы получите квадратный, в основном, смежный остров с, по крайней мере, несколькими крошечными субостровами у краев.
Конечно, если вы не возражаете против возможности разъединения ландшафта, несколько больший коэффициент масштабирования должен дать вам больше воды и более естественную форму острова. Регулировка уровня моря и / или масштаба вашей исходной карты высот также может использоваться для изменения размера и формы результирующих островов.
источник