Карты нормалей для спрайтов

Титульная иллюстрация статьи про спрайты с картами нормалей

Написано

в

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

Для этого используются обычные текстурные карты нормалей, только наложенные вместо 3D-модели на плоский спрайт. Технически это приём примитивный, и рассказать хочу не о нём, а о том как эти текстуры нормалей можно приготовить.

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

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

Варианты

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

Можно класть в программу не сам спрайт, а соответствующую ему карту высот. Но если я стану руками рисовать специальные карты, то нужен ли мне вообще генератор?

Покопав, я обнаружил в Krita даже несколько инструментов, применимых к моей задаче. Например фильтр Height to Normal Map интерпретирует изображение как карту высот и генерирует карту нормалей, в которой отражён соответствующий рельеф 👍.

Другой фильтр, Phong Bumpmap, имитирует трёхмерное освещение заданными источниками света по карте высот — если в этом фильтре определённым образом настроить красный, зелёный и синий источники, то можно получить грубый вариант карты нормалей.

Ещё есть фильтр Normalize для нормализации карт, который полезен вместе с предыдущим, и вообще когда карты создаются какими-нибудь импровизированными средствами.

Больше всего мне помогла специальная кисть 🖌, которая интерпретирует наклон пера как ориентацию поверхности и просто рисует в карту соответствующий цвет. Только стандартная кисть, которая уже есть в Krita, рисует нормали в правосторонней системе координат, а Godot (3.x) ожидает их в левосторонней.

Техническая минутка

Технически, карта нормалей — это просто текстура, только вместо цвета в каждом её пикселе записано в какую сторону «смотрит» поверхность в этом месте. Идея в том что, зная куда обращена поверхность и где находится источник света, игра может рассчитать сколько света или тени приходится на каждую точку спрайта ☝🤓.

Такое применение текстуры возможно, потому что в каждом пикселе всё равно записываются три числа. Просто движок использует их не как количество красного, зелёного и синего цвета, а как компоненты x, y и z трёхмерного вектора.

Иллюстрация помещения нормальных векторов в пиксели текстуры и освещения с их использованием

На текстурах нормалей можно видеть, что там где поверхность обращена вправо цвет более красный (больше значение x), а там где обращена вниз — более зелёный (больше значение y). Это позволяет «читать» карту нормалей глазами — достаточно представить что смотришь на рельеф, который освещается красным светом с правой стороны и зелёным светом снизу.

Пример карты нормалей

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

В любом случае, арифметика, которая вычисляет освещённость, предполагает что векторы в карте имеют вполне конкретную (единичную) длину (то есть различаются только направлениями). Поэтому карте после создания и может требоваться «нормализация» — если просто набросать какие-то числа в красный, зелёный и синий без согласования друг с другом, то длины у векторов получатся какие попало и освещение будет выглядеть криво 😣.

Кроме того, тот же вектор можно выражать в разных системах координат. Для карт нормалей это значит, что вертикальная (Y) компонента может отсчитываться или сверху вниз, или снизу вверх, в зависимости от того для какого графического движка эта карта предназначена.

Пример карт нормалей в левосторонней и правосторонней системе координат

Специальная кисть

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

Поэтому, чтобы рисовать карты нормалей, подходящие для Godot, я настроил отдельную кисть. К счастью в Krita есть заготовка, в которой достаточно выбрать в какую сторону направлены мои x, y и z.

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

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

Ещё варианты

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

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

Пример добавления на низкополигональную карту нормалей рисованных деталей, и результат

А ещё грубые карты нормалей можно снять с физических предметов. Выше я писал, что карта нормалей как бы изображает рельеф, освещённый красным источником света с правой стороны, и зелёным источником с нижней. Если обратить процесс, осветить предмет справа красным и снизу зелёным и сфотографировать его, то в снимке образуются вектора почти как в настоящей карте нормалей — красная (X) компонента будет больше там где поверхность обращена вправо, зелёная (Y) там где обращена вниз.

Пример пластилиновой фигурки, её снимков с освещением с четырёх сторон, и синтезированной из снимков карты нормалей

Только объект должен быть сплошного цвета (ну или перед съёмкой его нужно бахнуть светлой матовой краской из баллончика). А ещё, если предмет сложной формы, то он может отбрасывать тень сам на себя, и в неосвещённых местах карты получатся пробелы.

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

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *