Не так давно мы опубликовали материал о тестировании 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
Устранение угловатости
С волосами персонажа была очевидная проблема: шейдер вместе с текстурой создавал жесткие концы, а анизотропный блик подчеркивал выступающие друг за другом полигоны волос.
Из этого стали понятны требования к текстуре и модели:
— полигоны прядей не должны пересекаться там, где виден блик, — так он будет красиво ходить по волосам, не подчеркивая угловатость;
— верхние слои прядей волос должны быть более детализированными, чем нижние, чтобы создавать общую форму и эффект подвижности.
![Шейдер для волос туториального персонажа из Vikings: War of Clans Шейдер для волос туториального персонажа из Vikings: War of Clans](https://habrastorage.org/getpro/habr/upload_files/678/76b/c51/67876bc51fe93445ac186ca2f53b9d81.jpg)
Затем мы принялись за текстуру. Чтобы кончики волос смотрелись лучше, требовалась коррекция альфа-канала.
Здесь выбор стоял между двумя вариантами:
-
первый — использовать жесткую обрезку cutout-шейдером (т. е. фильтровать альфа-канал по пороговому значению, получая полностью прозрачный или непрозрачный пиксель, без промежуточных областей);
-
второй — использовать мягкий transparent-шейдер, который работает плавнее, но более требователен к железу.
![Cutout shader/Transparent shader Cutout shader/Transparent shader](https://habrastorage.org/getpro/habr/upload_files/9ab/0f9/eb7/9ab0f9eb7e1032f57142af2a578eb635.jpg)
Мягкий шейдер делал всего лишь одну дополнительную операцию — читку буфера кадра (для смешивания текущего пикселя волос с уже имеющейся на экране картинкой), за счет чего и достигался плавный уход в прозрачность. К тому же занимаемая экранная площадь прически нашего персонажа и количество наложений были невелики — так что особой разницы в ресурсозатратности между шейдерами в нашем случае не оказалось. Однако с мягким шейдером волосы смотрелись лучше.
Но тут была одна загвоздка — камера отрисовывала изображение в текстуру. Поэтому для того, чтобы оно корректно обрезалось по альфе и волосы отображались на текстуре, требовался дополнительный проход, который бы эту альфу создавал.
В итоге был написан шейдер, содержащий в себе три прохода. Первый записывает правильный силуэт прически в альфу текстуры, вместе с объемом волос и их правильной обрезкой на кончиках. Второй рисует cutout-геометрию, а третий (transparent) — добавляет мягкие кончики.
Реалистичный блик
Чтобы волосы выглядели живыми, они должны блестеть. Для достижения такого эффекта мы рассматривали два варианта: анизотропный динамический блик, реагирующий на освещение, и статичный фейковый, который мог бы облегчить обработку шейдера.
Для создания статичного блика мы использовали технологию текстурирования Matcap — имитацию материала и окружения с помощью всего одного изображения.
![Источник изображения Matcap Shader Источник изображения Matcap Shader](https://habrastorage.org/getpro/habr/upload_files/c4d/af1/5fd/c4daf15fd029dc11503b47718c9636be.jpg)
Эта технология позволяет натягивать предзапеченное в текстуре освещение на 3D-модель для имитации освещения (теней, бликов, оттенков и т. д.). То есть мы могли наложить поверх текстуры волос нарисованный блик так, как нам удобно. Форму блика можно настраивать, но он никак не будет реагировать на смену освещения.
Визуальная разница между анизотропным и фейковым бликами:
![Анизотропный блик Анизотропный блик](https://habrastorage.org/getpro/habr/upload_files/813/b6d/f6f/813b6df6f05c9dc11465ba05d4315c3a.jpg)
![Matcap-блик Matcap-блик](https://habrastorage.org/getpro/habr/upload_files/698/122/5f3/6981225f387677f150cbd548645cf2fe.jpg)
Разница в шейдерном коде при обработке анизотропного и фейкового бликов:
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 Normal map mask / Alpha specular mask](https://habrastorage.org/getpro/habr/upload_files/ec1/f99/c0d/ec1f99c0db377a301ef4dd568967359a.jpg)
Применение альфа-маски в коде:
fixed specDetail = tex2D(_MatcapTex, i.uv).b;
specDetail = pow(specDetail, _MatcapPow);
matcapSpec = matcapSpec * specDetail;
![](https://habrastorage.org/getpro/habr/upload_files/93d/877/e49/93d877e49a183423eba214bd483417d9.jpg)
Анимация и цветокоррекция
За основу мы взяли обычную костную анимацию для прядей. А для создания дополнительной динамики решили использовать шевеление в вертексном шейдере.
Для цветовой коррекции применили запеченное освещение в виде коэффициентов, которое называется SHA, или сферические гармоники. Такой метод рендеринга может создавать высокореалистичное затенение с относительно небольшими ресурсозатратами. Его основой служат предварительно рассчитанные коэффициенты, и он отлично подходит для рассеянного освещения от окружения (например, засветов от неба). Этот же принцип использовался не только для волос, но и для самой модели персонажа.
![](https://habrastorage.org/getpro/habr/upload_files/679/d63/ffb/679d63ffb38fbf75219c904c6bbe6e1e.gif)
Исходники:
Детализация геометрии |
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, |
В получившемся шейдере нет ничего революционного — техники, которые применялись для его создания, используются во многих других проектах. Но именно благодаря совокупности этих техник мы добились нужного результата.