16 April 2019

Создаём процедурные глобусы планет

Working with 3D-graphicsGame developmentGame design
Translation
Original author: metriximor
Искажения, бесшовный шум и как с ними работать.

image

Генерируем планету


Один из простейших способов генерации планеты — использование шума. Если мы решим выбрать его, то у нас есть пара возможных вариантов. Давайте рассмотрим каждый и определим лучший:

  • Шум Перлина (Perlin Noise) — самый простой вариант. Шум Перлина был разработан Кеном Перлином в 1983 году, он имеет пару недостатков — визуальные артефакты и довольно низкая по сравнению с другими вариантами скорость при генерации больших изображений.
  • Симплекс-шум (Simplex Noise) — разработан Кеном Перлином в 2001 году как попытка устранения недостатков шума Перлина; это вполне достойное и быстрое решение, однако обладающее серьёзным недостатком: использование трёхмерного симплекс-шума защищено патентом, что делает его довольно дорогостоящим.
  • Открытый симплекс-шум (Open Simplex Noise) — был разработан KDotJPG с одной простой целью: создать современную и бесплатную версию симплекс-шума, относительно быструю и без искажений.

Из этих трёх лично я предпочитаю Open Simplex Noise, который использую в своих личных проектах. Стоит заметить, что в текущей реализации OpenSimplexNoise для получения простого доступа к масштабу, октавам и порождающим значениям потребуется дополнительная работа. В Интернете есть множество информации о том, что делает каждый из этих элементов, и я крайне рекомендую вам её изучить. Однако в своей статье я буду говорить не об этом.


Вот как выглядит Open Simplex Noise с 16 октавами.

Бесшовный шум


Шум бесконечен, а значит, если мы просто создадим холст с соотношением сторон 2:1, чтобы получить равнопромежуточную проекцию, то она не будет зацикленной при наложении на сферу (выражаю благодарность этому потрясающему веб-сайту), а на горизонтальном шве и на полюсах будут огромные отличия.

image

Шум, созданный без учёта швов.

image

Заметьте огромные швы, появившиеся при наложении шума на сферу.

Исправить это можно множеством способов; например, в этом отличном посте Red Blob Games [перевод на Хабре] достаточно было просто сгенерировать остров с помощью функции, получающей в качестве переменной расстояние до центра и и задавая на краях высоту 0 для минимизации швов.

Однако нам нужно не это. Мы хотим сгенерировать планету с возможностью существования северного и южного полюсов, а для этого нам понадобятся более сложные математические вычисления.

Сферическое наложение


Метод, способный генерировать сферические планеты, заключается в преобразовании декартовых координат нашего холста в сферические координаты, генерации шума на основании этих координат, а затем в преобразовании шума в обратно в декартовы координаты и наложении его на холст.

Однако такая реализация имеет свои ограничения, причины возникновения которых показаны в потрясающем посте Рона Валстара. Самое главное — формы континентов в этом случае выглядят чрезвычайно странными и искажёнными, а поэтому мы не будем использовать этот вариант.

image

Сферическое наложение шума. Странные формы и искажения делают континенты довольно уродливыми.

image

Зато, по крайней мере, швов больше нет.

Кубическое наложение


В результате я использовал второй способ, взятый из поста Рона Валстара и серии статей acko Making Worlds. В них описывается генерация глобуса через генерацию куба и его «надувания», как будто он воздушный шар, до тех пор, пока он не пример форму сферы.


Изображение взято с acko.net. Оно объясняет концепцию кубической карты в простом для визуализации виде.

Теперь нам нужно просто сгенерировать шесть граней, что достаточно просто, есть множество способов сделать это.

В конце концов я решил создать массив и заполнить его данными. Я преобразовал 2D-координаты холста в 3D-координаты куба, а затем сгенерировал шум для каждой из этих 3D-координат так, чтобы сохранить их в соответствующее значение 2D-координаты.

//Z STATIC
for(int y = 0; y < cubeFaceSize; y++) {
	for(int x = 0; x < cubeFaceSize * 2; x++) {
		//Generates FRONT
		if(x < cubeFaceSize) {
			cubeMap[cubeFaceSize+x][cubeFaceSize+y] = noise.noise3D(x, y, 0);                    
		}
		//Generates BACK
		else {
			cubeMap[cubeFaceSize*3+(x-cubeFaceSize)][cubeFaceSize+y] = noise.noise3D(cubeFaceSize-(x-cubeFaceSize), y, cubeFaceSize);
		}
	}
}
//X STATIC
for(int y = 0; y < cubeFaceSize; y++) {
	for(int x = 0; x < cubeFaceSize * 2; x++) {
		//Generates LEFT
		if(x < cubeFaceSize) {
			cubeMap[x][cubeFaceSize+y] = noise.noise3D(0, y, cubeFaceSize-x);                   
		}
		//Generates RIGHT
		else {
			cubeMap[cubeFaceSize*2+(x-cubeFaceSize)][cubeFaceSize+y] = noise.noise3D(cubeFaceSize, y, x-cubeFaceSize);
		}
	}
}
//Y STATIC
for(int y = 0; y < cubeFaceSize * 2; y++) {
	for(int x = 0; x < cubeFaceSize; x++) {
		//Generates TOP
		if(y < cubeFaceSize) {
			cubeMap[cubeFaceSize+x][y] = noise.noise3D(x, 0, cubeFaceSize-y);          
		}
		//Generates BOTTOM
		else {
			cubeMap[cubeFaceSize+x][cubeFaceSize*2+(y-cubeFaceSize)] = noise.noise3D(x, cubeFaceSize, y-cubeFaceSize);
		}                
	}
}

Таким образом мы можем создать кубическую карту, которая легко преобразуется в равнопромежуточную проекцию с помощью замечательного кода, написанного Bartosz.

image

Сгенерированная алгоритмом кубическая карта.

image

Равнопромежуточное преобразование кубической карты.

image

Глобус кубической карты, отрендеренный на сайте maptoglobe.com.

Как видите, равнопромежуточная карта имеет гораздо более красивые формы, а при наложении на сферу она создаёт схожие со сферическим наложением результаты, не имея всех его недостатков. Кстати, равнопромежуточную проекцию можно легко преобразовать разными программами, например, NASA G.Projector, в практически любой тип карты.

В заключение


Генерация целой планеты может показаться устрашающей задачей, и хотя шум при его правильном использовании — это довольно мощный инструмент, он имеет свои проблемы, с которыми люди сталкивались на протяжении многих веков, например, наложение глобуса на 2D-холст с минимальными искажениями.

Предложенное мной решение создаёт очень грубо сгенерированные планеты, не учитывающие тектонических плит, рек, цепочек островов и даже гор, а потому его можно использовать только в качестве демонстрации или как основу для более сложных симуляций.

На самом деле, оно создаёт только матрицу значений в определённом интервале величин. Для изображений в градациях серого это интервал 0-255. Затем значения преобразуются в пиксель, создающий изображение, схожее с первым изображением в градациях серого, или в изображение в интервале от -11000 до 8000 для симуляции разности высот реального мира, после чего пиксели окрашиваются цветами в соответствии с интервалами высот (например, значения с 0 по 5 окрашиваются в цвет песка для симуляции побережья).

При построении Вселенной Бог использовал математику высшего уровня.

— Поль Дирак
Tags:картыпроцедурная генерацияшум перлинасимплекс-шумsimplex noiseopen simplex noisecubemap
Hubs: Working with 3D-graphics Game development Game design
+38
6.9k 128
Comments 7