Улучшаем промо-арты: переход из Unity в Blender

Меня зовут Рамиль Роозилехт, работаю старшим техническим художником в CM Games. В этой статье расскажу, как обновлял технический пайплайн по созданию промо-артов для гоночной игры Nitro Nation. Спойлер: через переход из Unity в Blender.

Новый инструментарий позволил нашей арт-команде сильно улучшить визуальную часть промо-материалов, а их создание теперь происходит заметно быстрее. Для сравнения:

Старый пайплайн работы

Раньше создавал промо-арты из скриншотов, сделанных в Unity. Мобильные шейдеры и простое освещение выглядели приемлемо на экране телефона, но плохо подходили для основы промо-материалов.

Сцена с запеченным светом в Unity. Если что-то меняется, нужно ждать, пока освещение пересчитывается.
Сцена с запеченным светом в Unity. Если что-то меняется, нужно ждать, пока освещение пересчитывается.
Промо после драфтовой дорисовки вручную.
Промо после драфтовой дорисовки вручную.

Минусы старого подхода:

  • Долго заниматься дорисовкой и фотобашингом поверх отснятых скриншотов. В среднем художник мог сделать не более 1,5 картинок за день.
  • Дорисованный материал часто выглядел хуже, чем арты более новых проектов у конкурентов.
Улучшаем промо-арты: переход из Unity в Blender
  • Невозможно вносить серьезные правки после начала дорисовки. Если в последний момент понадобится что-то изменить, сделать это будет непросто.
  • Сложно передавать исходники сцен. Если переиспользовать старую сцену для новых промо, то надо хранить её в основном репозитории проекта или делать экспорт всех мешей, материалов, текстур и сопутствующих элементов в Unity Package. Каждый объект в свою очередь тянет за собой скрипты, внешние плагины и другие зависимости, которые муторно отключать вручную.

Поиск альтернатив

Вначале собрался доработать существующий процесс съёмки материала. Добавил стандартный package для пост-эффектов Unity Post-Processing Stack. Благодаря нему можно применять пост-эффекты в эдиторе без запуска игры. Далее заменил оптимизированные мобильные шейдера на стандартные PBR материалы, чтобы получить улучшенную модель шейдинга и полный пакет текстур.

Эти изменения немного улучшили картинку, но собирать сцену стало дольше, ведь теперь нужно настраивать измененные материалы и перезапекать освещение. Также, часть эффектов из Post-Processing Stack, например, AO и SSR требовали deferred рендеринга и не были совместимы с forward рендерингом без дополнительной работы.

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

Открытая задача:выстроить процесс с ориентированием на художников и их софте. В идеале для этого нужен популярный, бесплатный 3D пакет с продвинутыми возможностями рендеринга. Основные варианты:

  • Unreal Engine. Есть только рендеринг, нужно искать готовые ассеты для сцен.
  • SketchUP. Есть только моделлинг, нужен плагин для рендеринга.
  • Blender. Полный пакет для создания контента.

Преимущества Blender над конкурентами:

  • Полностью бесплатный. У 2D художников обычно нет лицензий на Maya/3dsMax.
  • Open Source и задокументированный API. Большой простор для скриптинга.
  • Постоянные апдейты и улучшения. Одно из самых активных FOSS коммьюнити (улучшают продукт и добавляют свой контент).
  • Легковесность. Инсталлер <200мб, возможность портативного использования, поддержка Windows/Mac/Linux.

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

Исходники машин лежат в проекте как FBX’ы и хранят полный набор деталей авто в разобранном виде. Использовать эти файлы для рендеров неудобно, потому что нужно вручную собирать машину из частей. Но машина в игре уже собрана и готова к использованию, нужно только сохранить её. Для этого добавил пакет FBX Exporter в проект. Он поддерживает Binary/ASCII FBX форматы, его можно вызвать в скрипте или использовать из эдитора. Созданные им FBX’ы сохраняют оригинальные ссылки на текстуры и иерархию префаба.

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

<p>Машина сразу после импорта без материалов и текстур. В таком виде не получится использовать её для промо.</p>

Машина сразу после импорта без материалов и текстур. В таком виде не получится использовать её для промо.

Чтобы привести машину на скриншоте в презентабельный вид, надо:

  1. Настроить альфу для текстур. На скриншоте видны прозрачные шины и сетка радиатора. В части текстур альфа канал не используется, в других — может использоваться как альфа или как карта свечения. Blender смотрит на формат текстуры при импорте, и если альфа-канал существует, то автоматически подключает его к шейдеру. На самом же деле альфа в качестве прозрачности используется только у лейблов, а с остальных текстур её нужно отключить.
  2. Создать новые материалы. При импорте оригинальные материалы теряют свои настройки, поэтому нужно сделать их альтернативы на shading nodes. Список материалов стандартный, например, авто-краска, резина, хром, пластик, стекло.
  3. Применить новые материалы на все элементы машины. Большая часть элементов машин содержит название материала в именах, так что тут достаточно выбрать все одинаковые элементы и назначить им материал.
  4. Почистить мусор в иерархии. Нужна лишь геометрия и базовая иерархия, всё остальное можно убрать, чтобы упростить навигацию.
  5. Проверить на соответствие с моделькой в игре.

Звучит как огромная трата времени, особенно если делать вручную ежедневно.Но используя Python API для скриптинга, можно автоматизировать процесс конвертации машины. Тогда художнику останется лишь проверить результат на соответствие с игрой.

Работа со скриптами в Blender

Сверяюсь с документацией по API, чтобы не писать код вслепую. Скрипты для Blender можно писать прямо в самом файле. Для работы со скриптами доступно 3 редактора:

  • Python Console. Простая консоль с авто-заполнением команд по нажатию на Tab. Полезно, чтобы посмотреть синтаксис команд.
  • Info. Лог, где большинство действий пользователя отображаются в качестве Python команд. Получившийся код можно строками скопировать в скрипты.
  • Text Editor. Встроенный блокнот, из которого можно запускать скрипты и паковать текст прямо внутри .blend файла. В нём также доступны шаблоны для многих скриптов и аддонов. Например, батч-экспорт объектов, UI для создания кнопок\панелей\меню и новых операторов. Запустить открытый скрипт можно по кнопке Play ▶.

Создание аддона для автоматизации импорта авто

Для примера покажу функции, которые использую в аддоне для автоматизации импорта. Функционал такого аддона:

  • Удалить ненужные объекты
  • Загрузить библиотеку материалов, которая запакована в аддон
  • Заменить текущие материалы машина на материалы из библиотеки
  • Выставить корректные значения альфа-канала для текстур
  • Призвать риг водителя

Импорт коллекции из внешнего .blend файла, который упакован внутри аддона:

def importColl(collectionName):

В хедере скрипта должен быть import os, чтобы получить директорию файла

path=os.path.dirname(__file__) + "/collections/library.blend\\Collection\\" bpy.ops.wm.append(filename=collectionName, directory=path)

Удаление лишних объектов из иерархии:

def cleanUp():

List для поиска названий объектов под удаление:

RemoveStrings = ["Gear*","*flare*","UI*"]

Если выделен объект, снимаю выделение, чтобы не удалить лишнего:

if bpy.context.view_layer.objects.active != None: bpy.context.active_object.select_set(False) bpy.ops.object.select_all(action='DESELECT')

Пробегаю по списку, выделяю объекты и удаляю их.

for str in RemoveStrings: bpy.ops.object.select_pattern(pattern=str) print (str) bpy.ops.object.delete(use_global=False) bpy.ops.object.select_all(action='DESELECT')

Функция для фикса параметров в существующих материалах:

def fixImport():

Всем текстурам, кроме швов, выставляю режим альфа-канала:

for tex in bpy.data.images: if "seamTex" not in tex.name: tex.alpha_mode = 'CHANNEL_PACKED' for mat in bpy.data.materials: if 'Seam' in mat.name: continue

Материалам дисков и шин меняю значения metallic, roughness, specular

if 'Disk' in mat.name: mat.node_tree.nodes["Principled BSDF"].inputs[6].default_value = 1 mat.node_tree.nodes["Principled BSDF"].inputs[9].default_value = 0.25 if 'tyre' in mat.name: mat.node_tree.nodes["Principled BSDF"].inputs[7].default_value = 0.25 mat.node_tree.nodes["Principled BSDF"].inputs[9].default_value = 1

Для лейблов на машине выставляю Alpha Clip, остальным текстурам обрубаю линк ноды с текстурой, которая автоматически назначается в альфу шейдера.

if 'ables' in mat.name: mat.blend_method = 'CLIP' print (mat.name," set to clip") mat.use_nodes = True mat_node = mat.node_tree.nodes else: mat.use_nodes = True mat_node = mat.node_tree.nodes for node in mat_node: if "Image Texture" in node.name and node.outputs[1].links != None: for link in node.outputs[1].links: mat.node_tree.links.remove(link) if mat.blend_method == 'BLEND': if mat.name not in ['Glass', 'Glass_Clear']: mat.blend_method = 'OPAQUE'

Функция для замены оригинального материала на материал из библиотеки, которая импортируется в начале и содержит необходимые для ссылки объекты. Запакована в try/except pass, чтобы скрипт не падал, если не найдется нужного объекта.

Например, если на машине не будет хрома или карбона.

def applyMaterial(objectName,searchPattern): try: source = bpy.context.scene.objects[objectName] bpy.context.view_layer.objects.active = source source.select_set(True) bpy.ops.object.select_pattern(pattern=searchPattern) for ob in bpy.context.selected_objects: if "able" in ob.name: ob.select_set(False) bpy.ops.object.make_links_data(type='MATERIAL') bpy.ops.object.select_all(action='DESELECT') except:

Но если действительно не нашлось объектов, можно увидеть в консоли

print(objectName,"- failed.") pass

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

Улучшаем промо-арты: переход из Unity в Blender

Создание промо-арта по новому пайплайну

Рассмотрю создание экрана загрузки с Ford Mustang RTR-X. Исходных требований немного — авто главное в кадре, машина должна дрифтить. Так как это арт на загрузочный экран, размер надо взять с запасом, а композиция должна нормально обрезаться под все основные форматы экранов — 18:9, 16:9, 4:3.

Вызываю авто для экспорта. Для примера, машина в гараже.
Вызываю авто для экспорта. Для примера, машина в гараже.
<p>Машина готова к рендерингу после импорта в Blender и конвертации с помощью аддона.</p>

Машина готова к рендерингу после импорта в Blender и конвертации с помощью аддона.

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

Далее добавляю дым на сцену в качестве OpenVDB симуляции, анимирую машину вместе с камерой, поставив вместе на кривую и сдвинув на пару кадров. Это позволит получить “честный” Motion Blur на рендере, чтобы не добавлять его позже в Photoshop вручную.

<p>Финальный арт после небольшого композитинга и колор-коррекции.</p>

Финальный арт после небольшого композитинга и колор-коррекции.

Плюсы подобного подхода

Собирая промки в Blender, художники могут пользоваться широким перечнем фичей, невозможных для игрового движка. Например:

  • Выбор между Realtime рендером в Eevee или точным Path Tracing в Cycles
  • Shading Nodes для создания процедурных материалов
  • Честный Motion blur (анимируя положения камеры и автомобиля)
  • OpenVDB волюметрики для эффектов тумана, дыма, огня
  • Geometry Nodes для создания point clouds (например, пыль или капли на авто)
  • Light Groups для тонкой настройки освещения после рендера
  • Render Passes для финального композитинга

Отзывы о новом пайплайне

Новый пайплайн создания промо-арта сильно облегчил нашим художникам работу и освободил время для других задач. Теперь нам не нужно тратить время на дорисовку арта, а рабочий процесс стал походить на работу фотографа — как лучше поставить свет, попробовать разные ракурсы для съемки. Время итераций сильно сократилось, потому что рендерам необходима лишь минимальная обработка — чаще всего достаточно Camera Raw фильтра, и можно уже резать арт под необходимые размеры.

Маркетинг-команде тоже лучше — для главных распродаж в году можно запросить больше картинок хорошего качества вовремя. И правки делать проще — достаточно перерендерить картинку, а в Photoshop заменить только смарт-объект. Ничего не придётся перерисовывать.

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

4949
7 комментариев

Решение глупое - можно было сделать отдельную сборку для рендера на ПК для юнити и рендерить спокойно в 4к-8к с нужными настройками ГРАФИКИ


На анриле так все делают...

1
Ответить

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

3
Ответить

Художники такие:

Новый пайплайн создания промо-арта сильно облегчил нашим художникам работу

Ответить

... и освободил время для других задач.

3
Ответить

Картинки красивые, игра скучнейшая.

1
Ответить

Вот такое не работает чет

if "Image Texture" in node.name and node.outputs[1].links != None:

Вот так работает

if "Image Texture" in node.name and node.outputs[1].links:

Blender 2.93

Ответить