Gamedev Codisrq
1 652

Способы текстурирования ландшафта

Привет! Если ты увлекаешься процедурным генерированием ландшафта, эта статья поможет тебе в процедурном его текстурировании.

В закладки
Аудио

Так, как я специализируюсь на Unity - буду рассказывать всё то, что косвенно или прямо связано с ним.

Поехали!

Первый способ - Texture Splatting

В этом случае мы создаём Shader, который имеет 4 канала, такие, как R - красный, G - зелёный, B - синий, A - прозрачность. Если ландшафт очень большой - мы делим его на отдельные регионы, где каждый регион имеет по 4 канала.

R + G + B + A = RGBA

При текстурировании ландшафта мы записываем каналы исходной текстуры в соответствующие каналы для Shader'а. То есть красный цвет исходной текстуры записывается в R-канал, зелёный, синий и прозрачность точно также.

Такой способ хорошо экономит память, но в производительности сильно отстаёт от следующего кандидата. Тест на производительность:

Я как-то замерял, у меня каждая новая текстура заметно просаживала производительность. Вот заметка, например: Измеряю градацию FPS. Делаю так: добавляю по одной текстуре на ландшафт, запускаю сцену в редакторе и в сборке. FPS меряется компонентом FPS Counter. В редакторе измеряю чисто для сравнения. И только после разогрева, не сразу после запуска Unity.

1) Без текстур - 67/88

2) 1 текстура без карт нормалей - 64/82

3) 1 текстура с картой нормалей - 57/72

4) 2 текстуры с картами нормалей - 49/58

5) 4 разные текстуры с картами нормалей - 37/43

6) 4 одинаковые текстуры с картами нормалей - 38/43

7) 5 текстур с картами нормалей - 36/41

8) 8 текстур с картами нормалей - 36/41

Второй способ - "Всё в одном"

Да, второй способ не знаю как назвать (и есть ли у него название?). Потому, как ранее не встречал статью или мануал по данному способу, а пришёл к этому вынуждено.

Нам понадобится один Shader, у которого есть только текстура и карта нормали. Shader очень простой и выводит данные как есть. Второй Shader - это, так называемый, Blit Shader, который будет смешивать текстуры между собой и выводить "скомпонованный" результат, как одну текстуру.

Такой способ не нуждается в вычислениях и сразу выводит (заранее подготовленную текстуру) на ландшафт.

Помимо простого смешивания текстур, мне нужно было смешивать текстуры на определённой высоте ландшафта, например трава - на равнинах, а камень - на склонах. Не представляю как мне удалось бы сделать такое первым способом, но для этого способа я знаю решение:

  • Необходимо генерировать ландшафт так, чтобы порядок вершин в массиве располагались по формуле y + x * height.
  • Находим самую высокую и низкую точку ландшафта, назовём вектор (x, y) как Elevation.
  • Создаём массив heightMap и последовательно заполняем его высотой вершин по формуле Mathf.InverseLerp(Elevation.x, Elevation.y, vertex.y).
  • Создаём и заполняем массив heightMapColors, на каждый (RGB) канал записываем значение heightMap. Прозрачности не должно быть.
  • Создаём Texture2D и красим его через SetPixels(heightMapColors).

Передаём данную текстуру нашему Blit Shader'у, а в нём в свою очередь выполняем смешивание исходя из данной текстуры. Результат:

Texture1 + (HeightMap + Texture2) = Texture3

Если будут вопросы пиши в Telegram.

Наш канал о разработке игр.

With love - @codisrq.

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Codisrq", "author_type": "self", "tags": [], "comments": 13, "likes": 40, "favorites": 145, "is_advertisement": false, "subsite_label": "gamedev", "id": 42269, "is_wide": false, "is_ugc": true, "date": "Fri, 08 Mar 2019 20:28:16 +0300" }
{ "id": 42269, "author_id": 113347, "diff_limit": 1000, "urls": {"diff":"\/comments\/42269\/get","add":"\/comments\/42269\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/42269"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64954, "last_count_and_date": null }

13 комментариев 13 комм.

Популярные

По порядку

Написать комментарий...
2

Зачем стараетесь, можете уже тупо вбрасывать ссылку на телеграм канал, смысл и посыл будет тот же

Ответить
7

Это было бы грубо с моей стороны, простой не информативный спам.
В дальнейшем хочу поделиться способом текстурирования который использовался в Warcraft III.
Где каждый тайл/тайлсет имеет маску, которая поможет сделать текстуру объёмным на ландшафте. Это ведь не каждый знает и мало где написано на русском/родном языке...

Ответить
0

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

Ответить
0

В Blit Shader'е можно изменить и tiling и offset текстуре, а затем вернуть результат. Каждая текстура проходит постобработку этим шейдером, а затем это всё "скомпоновывается" в одну единую текстуру.

Ответить
0

Разные скейлы для текстуры травы:
https://imgur.com/a/WCFT9ja

Ответить
2

Substance Designer умер?

Ответить
0

Нет, конечно. не умер :D. Мануал для тех, кто любит процедурную генерацию ландшафта, с задаваемыми параметрами прям из редактора Unity.

Ответить
1

Очень странно, заголовок о способах текстурирования, а статья про инструкцию как сделать равнины и горы.
Причем странная интсрукция.
1. Зачем создавать вектор с наибольшими высотами, если это не понятно что это он
"Значит щас используем наибольшую высоту. Хмм elevation.x.. *через какое-то время* "что такое elevation и где мне взять максимальные высоты".
Не проще ли использовать heightMin, heightMax.
2. Зачем значения интерполировать? (Lerp)
3. Зачем три канала заполнять одними и теми же данными. Смысл вообще в этом, когда в самом начале подпункта говорилось о слое маске. Типо маска для каждого слоя? Но это не очень то и логично.
4. Почему всего пара вещей (первый подпункт) так просаживало фпс? В обычной ситуации их же сотни и тысячи

В общем странная очень статья, похоже больше на это https://youtu.be/O80ziKCapdc

Ответить
0

1. HeightMin, HieghtMax или просто Vector2 - разница только в оформлении. В общем, тут можно полностью положиться на свой вкус и идею.

2. Зачем значения интерполировать? (InverseLerp)
- Это сделано затем, чтобы транслировать значения высот вершин (они обычно в диапазоне 0.05 - 0.2 у ландшафтов) в удобный для настроек вид. После интерполяции значения будут в диапазоне 0.0 - 1.0, что гораздо удобней и привычней, чтобы в дальнейшем задать высоты, на которых будет располагаться текстура.

3. Зачем заполнять три канала одними и теми же данными?
- Дело в том, Shader'у нельзя передать одномерный массив, который использовался бы как карта высот, для смешивания текстур. А так, как идея была "представить" карту высот как чёрно белую маску, мы наполнили все три канала. Конечно, можно не наполнять все три канала одними и теми же данными, а наполнить только один его канал, например R-красный, но тогда в этом случае вам бы всё равно пришлось создавать массив Color, и наполнить, либо один его канал, либо все три, а затем передать массив Color текстуре - тут маленькая разница в производительности, на памяти это не играет роли.

4. Почему всего пара вещей (первый подпункт) так просаживало FPS?
- Извините, но я не знаю по какому прицепу работает ландшафт в Unity, единственное, что - это Texture Splatting. Я всего лишь транслировал данные как есть.

Это моя первая статья, но в следующий раз я учту и подобные вопросы, спасибо за них отдельно :)

Ответить
1

сори но материал ламеский

Ответить
0

-

Ответить
0

Очень странный материал. Не написано даже про банальный трипланар, про vertex paint (что уже подметили выше). То, что описано тут, можно было бы сделать, просто подав хитмапу и мешая текстуры по ней. Нормалмап, хотя они тут всё время упоминаются, я вообще не увидел.

Ответить
0

Это всё процедурное, тут нет vertex paint'ов. То есть всё "раскрашивание" происходит программно, лишь с некоторыми заданными параметрами: высота размещения текстуры, тайлинг, оффсет, шум и карта высот.

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

Ответить
0

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjog" } } }, { "id": 10, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-250597-0", "render_to": "inpage_VI-250597-0-1134314964", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=clmf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudo", "p2": "ftjf" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvc" } } } ]
Узнавайте новости о мостах
Санкт-Петербурга первыми
Подписаться на push-уведомления