Импорт сгенерированной локации в Unity: от GLB до играбельной сцены

Сгенерированная локация в GLB или FBX попадает в Unity за минуты. Играбельной её делают настройки импорта, коллайдеры и свет — вот весь путь.

Городок с улицами, переходами, кирпичными домами и коттеджами, построенный агентом в Cuberta

GLB на 60 МБ с целым городком попадает в папку Assets, и Unity его просто не замечает. Или замечает — и здания размером с монету. Или размер верный, всё залито пурпурным, а персонаж проваливается сквозь тротуар. Это стандартные первые двадцать минут со сгенерированным окружением, и за каждым шагом стоит конкретная настройка с именем.

Дальше предполагается, что у вас есть GLB или FBX с локацией и более-менее пустой проект Unity. Цель — сцена, по которой можно ходить, а не скриншот.

Как завести файл в проект

Импортер моделей Unity из коробки читает FBX, OBJ, DAE и DXF. glTF и GLB — нет: встроенного импортера для них не существует, поэтому .glb, перетащенный в чистый проект, так и лежит мёртвым файлом. Импортер добавляется пакетом.

Пакет называется Unity glTFast, и по названию в списке Package Manager он не показывается: Window > Package Manager, кнопка +, Add package by name, и com.unity.cloud.gltfast. Свежие версии требуют Unity 2021.3.46f1 или новее и работают с Built-in, Universal и High Definition. После установки .gltf и .glb импортируются как любая другая модель — префабом, где меши, материалы и текстуры лежат вложенными ассетами.

Что экспортировать, если инструмент даёт выбор, зависит не столько от самих форматов, сколько от того, что вы будете делать после импорта. Cuberta, например, пишет GLB или FBX вместе с текстурами, так что это выпадающий список, а не отдельный этап конвертации.

Что вам нужноЭкспортПочему
Запекать лайтмапыFBXРодной импортер Unity генерирует второй UV-набор; у glTF-импортеров с этим непостоянно
Чтобы текстуры приехали вместе с файломGLBОни внутри бинарника — перепривязывать нечего
Переназначить сотни слотов материалов разомFBXНа вкладке Materials есть Search and Remap
Самый короткий цикл итерацийGLBОдин файл наружу, один внутрь, без папки текстур

Спор о самих форматах — отдельная тема. Здесь годятся оба; таблица про то, какой обойдётся дешевле потом.

Масштаб и как проверить его за полторы минуты

Ошибка в сто раз

Физика Unity считает одну мировую единицу одним метром, и на этом держатся все контроллеры, значения гравитации и коэффициенты трения. FBX несёт собственный масштаб единиц внутри файла, и множество экспортёров пишут туда сантиметры. Галка Convert Units на вкладке Model читает объявленный масштаб и пересчитывает его; поле Scale Factor — ручной множитель на случай, когда объявление было неверным. Городок, приехавший в сто раз больше нужного, почти всегда упирается в эти два поля. Опция Bake Axis Conversion на той же вкладке запекает поправку по оси «вверх» прямо в данные меша, а не прячет её в повёрнутом корневом объекте.

glTF описан в метрах, поэтому GLB редко приезжает с неправильным масштабом, — зато он правосторонний там, где Unity левосторонняя, и импортеры решают это отражением одной оси. Если городок выглядит правильно, но читается зеркально: надписи наоборот, дорога, которая заворачивала направо, теперь заворачивает налево, — вы смотрите именно на это.

Мерить контроллером персонажа, а не глазом

Прежде чем строить что-то поверх импорта, поставьте в сцену CharacterController — высота 2, радиус 0,5, step offset 0,3, slope limit 45, — напишите ему десять строк движения и пройдитесь по локации. Четыре вещи говорят почти всё:

  • Дверные проёмы пропускают капсулу в 2 м с запасом над головой, а не впритирку.
  • Бордюры 0,1–0,15 м, чтобы step offset поднимал вас без прыжка.
  • Подступенки лестниц ниже step offset, иначе вы никуда не заберётесь.
  • Полоса дороги 3–3,5 м, примерно три шага поперёк.

Если ради дверей приходится ставить Scale Factor 0,35, а тротуары после этого выглядят взлётной полосой, проблема в пропорциях, а не в единицах, и никакая настройка импорта её не чинит.

Материалы: что переживает переезд, а что вы соберёте заново

Шейдеры выбирает тот пайплайн, в котором вы сейчас

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

Кроме того, импортер подбирает шейдеры под пайплайн, активный в момент импорта. Импортируйте GLB в проект на Built-in, потом переключитесь на URP — и все материалы из этого файла станут пурпурными. Выходов два, и выбор зависит от того, чьи это шейдеры:

  • Штатные шейдеры Unity — обычный случай для FBX, который садится на Standard или URP/Lit. Откройте Window > Rendering > Render Pipeline Converter, укажите исходный и целевой пайплайн и запустите конвертеры материалов. Кастомные шейдеры он не трогает, а изменения необратимы, так что сначала коммит.
  • Собственные шейдеры glTF-импортера — для них у конвертера нет соответствий. Сначала настройте пайплайн, потом правый клик по ассету и Reimport.

Собираете материал руками — следите за каналами

glTF кладёт roughness в зелёный канал, а metallic — в синий одной текстуры; окклюзия, когда делит с ними карту, лежит в красном. Стандартный шейдер Unity ждёт metallic в красном, а smoothness — единица минус roughness — в альфе. Положите glTF-карту metallic-roughness в слот Metallic у URP/Lit, и поверхность будет неправильной дважды: не тот канал и инверсия. Собственные шейдеры импортера читают раскладку glTF напрямую — вот почему замена их на URP/Lit «чтобы было аккуратнее» превращает весь городок в пластик.

Два флага текстур ломаются молча. Карты нормалей должны иметь тип Normal Map: принятая за цветные данные, карта нормалей не выглядит сломанной — она выглядит плоской. А у любой упакованной маски галку sRGB нужно снять, иначе значения декодируются по гамме и всё выглядит чуть более блестящим. На стороне FBX всё это живёт на вкладке Materials: поставьте Location в Use Embedded Materials, нажмите Extract Materials и Extract Textures, а затем через Search and Remap одним проходом направьте сотни слотов на вашу библиотеку материалов.

Коллайдеры: меш-коллайдеры надо снять

Сгенерированная геометрия и Mesh Collider — плохая пара, и причина здесь не во вкусе, а в жёстком ограничении движка. Меш-коллайдер либо выпуклый, либо нет. Невыпуклый работает только на статике: повесьте Rigidbody — и Unity откажет. Выпуклый ограничен 255 треугольниками, а в сгенерированном доме их от трёх до тридцати тысяч. То есть видимый меш здания годится в коллайдеры только если здание никогда не двигается, — и даже тогда вы просите физику перебирать каждый треугольник фасада, чтобы сообщить игроку, что он упёрся в стену.

Снимите Generate Colliders при импорте и пройдите по сцене осознанно:

  • Земля, дороги, рельеф — невыпуклый Mesh Collider на видимом меше допустим, если это одно более-менее плоское полотно. Низкополигональный меш столкновений лучше; один бокс под плоским кварталом ещё лучше.
  • Здания, внутрь которых вы не заходите — по одному Box Collider на пятно застройки, по габаритам рендерера.
  • Здания, внутрь которых заходите — боксы на стены, пол и потолок, а дверные проёмы — промежутки между ними. Настраивать дольше, на рантайме дешевле на порядок.
  • Деревья, фонари, знаки, столбики — Capsule Collider на ствол или стойку и ничего на крону. Игроки рассчитывают проходить под ветками.
  • Бордюры и низкие ступени — часто вообще ничего. Step offset переносит контроллер через них, а коллайдер там только заставляет спотыкаться.
  • Всё, что с Rigidbody — примитивы или выпуклый меш до 255 треугольников. Третьего варианта нет.
Коллайдеры описывают, куда игрок может пройти, а не то, как объект выглядит. Собор получает бокс.

На четырёхстах объектах этот проход стоит заскриптовать: редакторный скрипт обходит иерархию, вешает BoxCollider по границам каждого Renderer и пропускает ваши префиксы растительности — почти всё готово меньше чем за минуту. Работает это только если локация приехала отдельными объектами, а не одним сваренным мешем, и это стоит проверить до экспорта: в агентном редакторе вроде Cuberta каждое здание и каждый пропс остаются выделяемым объектом. Заодно оставьте включённой Prebake Collision Meshes в Player Settings: готовка меш-коллайдеров на рантайме проявляется провалом кадра в момент загрузки сцены.

Свет: недостающий второй UV-набор

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

Для FBX лечится одной галкой: Generate Lightmap UVs в настройках импорта модели создаёт второй канал. Цену стоит знать до того, как включать это на трёхстах мешах.

  • Время импорта. Развёртка нескольких сотен мешей занимает минуты и повторяется при каждом реимпорте.
  • Эффективность текселей. Автоматическая упаковка рыхлее ручной развёртки, поэтому на тот же результат уходит больше разрешения лайтмапы.
  • Геометрию это не чинит. Пересекающиеся и вырожденные треугольники в источнике продолжат протекать и после развёртки.

glTF-импортеры ведут себя непостоянно вплоть до полного отсутствия генерации лайтмап-UV, и это практическая причина везти в Unity именно FBX, когда вы заранее знаете, что будете запекать. Если вы всё же на GLB, сгенерируйте канал редакторным скриптом через Unwrapping.GenerateSecondaryUVSet на каждом меше.

Прежде чем воевать с развёрткой, взвесьте альтернативу. В Unity 6 Adaptive Probe Volumes дают URP и HDRP запечённое непрямое освещение без лайтмап-UV и без ручной расстановки пробов: Unity распределяет их по плотности геометрии. Для сгенерированной локации, где вы всё равно не управляете UV, это чаще выгодный обмен. Отдаёте вы тонкую детализацию запечённых теней на статике: пробы несут непрямой свет, а не резкие контактные тени.

Где большая локация начинает болеть

Четыреста объектов — это четыреста draw call ещё до того, как в сцену вошёл персонаж, а сгенерированный городок набирает эту цифру быстро. Основную работу делают три настройки:

  • Флаги Static. Помечайте всё неподвижное как Static. Это даёт статический батчинг — Unity сливает меши в общие буферы по 64 000 вершин каждый, — плюс occlusion culling и участие в GI. Плата — память: слитые буферы это дополнительные копии геометрии, и городок из уникальных мешей легко добавляет сотни мегабайт.
  • GPU-инстансинг. Если генератор переиспользовал один фонарь шестьдесят раз, включите Enable GPU Instancing на его материале, и шестьдесят схлопнутся в считаные вызовы. Уникальным зданиям это не даёт ничего. Учтите приоритет: Unity отключает инстансинг на рендерере, который успешно попал в статический батч, поэтому одинаковые пропсы бывают либо статикой, либо инстансами.
  • Read/Write. Держите выключенным, если данные меша не читаются на рантайме: включённый флаг хранит вторую копию каждого меша в памяти CPU.

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

Пять поломок, на которые натыкаются на самом деле

  1. Всё пурпурное. На материалах шейдеры другого пайплайна. Реимпорт с активным целевым пайплайном или Render Pipeline Converter, если шейдеры штатные.
  2. Городок в сто раз больше или меньше. Масштаб единиц FBX: Convert Units и Scale Factor на вкладке Model, а проверка — капсулой 2 м, а не глазом.
  3. Персонаж проваливается сквозь землю. Коллайдеров нет: glTF не несёт их вовсе, а Generate Colliders — опция FBX, которую вы, скорее всего, сознательно не включали. Делайте проход по коллайдерам.
  4. Запекание чёрное или в пятнах. Нет второго UV-набора. Generate Lightmap UVs для FBX, редакторный скрипт для GLB — или переход на Adaptive Probe Volumes.
  5. Play mode выдаёт 12 fps на пустой сцене. Draw call и тени. Пометьте статику, снимите отбрасывание теней с мелких пропсов и прочитайте окно Stats, прежде чем гадать.

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