В 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-игре нужны карты нормалей — например, пиксельарт с динамическим освещением на мой вкус выглядит отвратительно — но если они нужны то получить их можно самыми разными способами.

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