Шейдер для волос туториального персонажа из Vikings: War of Clans
Не так давно мы опубликовали материал о тестировании Unity Cloth на мобильных устройствах. В другой статье на render.ru рассказали о том, как арт-отделу Plarium Krasnodar удалось сделать модель персонажа с высокой детализацией и при этом не перегрузить проект. Теперь давайте поговорим о работе над шейдером и анимацией для волос этой модели.
ТЗ
Перед командой Graphic Development стояли следующие задачи:
определить допустимый уровень детализации геометрии;
устранить угловатость модели и текстуры;
создать реалистичный блик;
придать прическе подвижности.
Сначала мы решили определить требования к модели.
Детализация геометрии
Особых требований к детализации геометрии выявлено не было (как обычно — чем меньше вершин, тем лучше). Для замера производительности мы брали тестовый незаскиненный исходник и с помощью 3D-пакета с использованием модификаторов Smooth увеличивали детализацию.
Детализация | PlayerLoop (ms) mediana |
1396 вертексов | 33.15 |
4423 вертексов | 33.2 |
15537 вертексов | 33.1 |
58003 вертексов | 33.2 |
Показания взяты с устройства Android Samsung Galaxy Tab S2
Устранение угловатости
С волосами персонажа была очевидная проблема: шейдер вместе с текстурой создавал жесткие концы, а анизотропный блик подчеркивал выступающие друг за другом полигоны волос.
Из этого стали понятны требования к текстуре и модели:
полигоны прядей не должны пересекаться там, где виден блик, — так он будет красиво ходить по волосам, не подчеркивая угловатость;
верхние слои прядей волос должны быть более детализированными, чем нижние, чтобы создавать общую форму и эффект подвижности.
Затем мы принялись за текстуру. Чтобы кончики волос смотрелись лучше, требовалась коррекция альфа-канала.
Здесь выбор стоял между двумя вариантами:
первый — использовать жесткую обрезку cutout-шейдером (т. е. фильтровать альфа-канал по пороговому значению, получая полностью прозрачный или непрозрачный пиксель, без промежуточных областей);
второй — использовать мягкий transparent-шейдер, который работает плавнее, но более требователен к железу.
Мягкий шейдер делал всего лишь одну дополнительную операцию — читку буфера кадра (для смешивания текущего пикселя волос с уже имеющейся на экране картинкой), за счет чего и достигался плавный уход в прозрачность. К тому же занимаемая экранная площадь прически нашего персонажа и количество наложений были невелики — так что особой разницы в ресурсозатратности между шейдерами в нашем случае не оказалось. Однако с мягким шейдером волосы смотрелись лучше.
Но тут была одна загвоздка — камера отрисовывала изображение в текстуру. Поэтому для того, чтобы оно корректно обрезалось по альфе и волосы отображались на текстуре, требовался дополнительный проход, который бы эту альфу создавал.
В итоге был написан шейдер, содержащий в себе три прохода. Первый записывает правильный силуэт прически в альфу текстуры, вместе с объемом волос и их правильной обрезкой на кончиках. Второй рисует cutout-геометрию, а третий (transparent) — добавляет мягкие кончики.
Реалистичный блик
Чтобы волосы выглядели живыми, они должны блестеть. Для достижения такого эффекта мы рассматривали два варианта: анизотропный динамический блик, реагирующий на освещение, и статичный фейковый, который мог бы облегчить обработку шейдера.
Для создания статичного блика мы использовали технологию текстурирования Matcap — имитацию материала и окружения с помощью всего одного изображения.
Источник изображения Matcap ShaderЭта технология позволяет натягивать предзапеченное в текстуре освещение на 3D-модель для имитации освещения (теней, бликов, оттенков и т. д.). То есть мы могли наложить поверх текстуры волос нарисованный блик так, как нам удобно. Форму блика можно настраивать, но он никак не будет реагировать на смену освещения.
Визуальная разница между анизотропным и фейковым бликами:
Анизотропный бликMatcap-бликРазница в шейдерном коде при обработке анизотропного и фейкового бликов:
Anisotrophic | Matcap |
fixed4 frag (v2f i) : SV_Target float HdotN = dot (halfDir, i.normal); float diffuseSpecular = lerp (1.0, specTex.g, _SpecularPower); | v2f vert (appdata v) fixed4 frag (v2f i) : SV_Target |
Визуальная разница между анизотропным и фейковым бликами в рамках сцены, где появлялась девушка, была слабо различима, да и угол, под которым ее видел игрок, менялся не сильно. Но первый вариант генерировал больше математических операций, поэтому мы выбрали второй.
Чтобы блик смотрелся органично и не создавал плоского эффекта, решили применить маску. Сначала мы пробовали вариант с картой нормали, но позже заменили ее менее тяжелой альфа-маской, которая давала похожий эффект.
Normal map mask / Alpha specular maskПрименение альфа-маски в коде:
fixed specDetail = tex2D(_MatcapTex, i.uv).b;specDetail = pow(specDetail, _MatcapPow);matcapSpec = matcapSpec * specDetail;
Анимация и цветокоррекция
За основу мы взяли обычную костную анимацию для прядей. А для создания дополнительной динамики решили использовать шевеление в вертексном шейдере.
Для цветовой коррекции применили запеченное освещение в виде коэффициентов, которое называется SHA, или сферические гармоники. Такой метод рендеринга может создавать высокореалистичное затенение с относительно небольшими ресурсозатратами. Его основой служат предварительно рассчитанные коэффициенты, и он отлично подходит для рассеянного освещения от окружения (например, засветов от неба). Этот же принцип использовался не только для волос, но и для самой модели персонажа.
Исходники:
Детализация геометрии | 2500 |
Количество текстур | 3 |
Разрешение текстуры 1 | 512×512 |
Разрешение текстуры 2 | 256×256 |
Разрешение текстуры 3 | 128×128 |
Шейдер:
Количество проходов | 3 |
Количество текстурных читок | 1 проход: 3 читки, 2 проход: 3 читки, |
Количество математических операций в вертексном шейдере | 1 проход: 54 math, 2 проход: 51 math, |
Количество математических операций во фрагментном шейдере | 1 проход: 11 math, 2 проход: 17 math, |
В получившемся шейдере нет ничего революционного — техники, которые применялись для его создания, используются во многих других проектах. Но именно благодаря совокупности этих техник мы добились нужного результата.