Шейдер для волос туториального персонажа из 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

Устранение угловатости

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

Из этого стали понятны требования к текстуре и модели:

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

Шейдер для волос туториального персонажа из Vikings: War of Clans

Затем мы принялись за текстуру. Чтобы кончики волос смотрелись лучше, требовалась коррекция альфа-канала. 

Здесь выбор стоял между двумя вариантами: 

  • первый — использовать жесткую обрезку cutout-шейдером (т. е. фильтровать альфа-канал по пороговому значению, получая полностью прозрачный или непрозрачный пиксель, без промежуточных областей);

  • второй — использовать мягкий transparent-шейдер, который работает плавнее, но более требователен к железу. 

Cutout shader/Transparent shader
Cutout shader/Transparent shader

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

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

В итоге был написан шейдер, содержащий в себе три прохода. Первый записывает правильный силуэт прически в альфу текстуры, вместе с объемом волос и их правильной обрезкой на кончиках. Второй рисует cutout-геометрию, а третий (transparent) — добавляет мягкие кончики. 

Реалистичный блик

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

Для создания статичного блика мы использовали технологию текстурирования Matcap — имитацию материала и окружения с помощью всего одного изображения.

Источник изображения Matcap Shader
Источник изображения Matcap Shader

Эта технология позволяет натягивать предзапеченное в текстуре освещение на 3D-модель для имитации освещения (теней, бликов, оттенков и т. д.). То есть мы могли наложить поверх текстуры волос нарисованный блик так, как нам удобно. Форму блика можно настраивать, но он никак не будет реагировать на смену освещения. 

Визуальная разница между анизотропным и фейковым бликами:

Анизотропный блик
Анизотропный блик
Matcap-блик
Matcap-блик

Разница в шейдерном коде при обработке анизотропного и фейкового бликов:

Anisotrophic

Matcap

fixed4 frag (v2f i) : SV_Target
{
float3 specular;
float3 binormal = normalize (cross( i.normal, i.tangent.xyz ));
float3 tangent = normalize(mul(unity_ObjectToWorld, i.tangent));

float HdotN = dot(halfDir, i.normal);
float VdotN = dot(i.viewDir, i.normal);
float anX = dot(halfDir, binormal) / _AnisotropicX;
float anY = dot(halfDir, tangent) / 4;

float diffuseSpecular = lerp(1.0, specTex.g, _SpecularPower);
specular = diffuseSpecular *_LightColor * _Specular * _SpecularColor * exp (-2.0 * (anX * anX + anY + anY) / (1.0 + HdotN ));
}

v2f vert (appdata v)
{

float3 worldNorm = UnityObjectToWorldNormal(v.normal);
worldNorm = mul((float3x3)UNITY_MATRIX_V, worldNorm);
o.matcapUV = worldNorm.xy * 0.5 + 0.5;

}

fixed4 frag (v2f i) : SV_Target
{

fixed specularMask = tex2D(_MatcapTex, i.uv).g;

}

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

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

Normal map mask / Alpha specular mask
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 читки,
3 проход: 1 читка

Количество математических операций в вертексном шейдере

1 проход: 54 math, 2 проход: 51 math,
3 проход: 13 math

Количество математических операций во фрагментном шейдере

1 проход: 11 math, 2 проход: 17 math,
3 проход: 0 math

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

 

Источник

3d graphics, animation, development, graphics, plariumkrasnodar, vikingswarofclans, блики, персонаж, текстуры, шейдер

Читайте также