Всем привет! Меня зовут Григорий Дядиченко, я СТО Foxsys, и я всё ещё люблю графику. В прошлый раз я рассказывал, что неплохим упражнением является сборка различных базовых материалов для тренировки в создании интересных эффектов. Давайте сегодня проведём такое упражнение вместе и разберём, каким образом можно получить в Unity такой материал как акрил, который будет работать в AR и на мобильных платформах. Кому интересно - добро пожаловать под кат!

Результат

Для начала небольшое видео с результатом, чтобы было понятно, что мы будем делать. Так же в данном репозитории лежит всё решение с исходниками. А теперь пожалуй начнём.

Какой акрил будем делать?

Когда-то давно майкрософт выпустил ролик с замечательной дизайн концепцией Microsoft fluent design. Там показаны достаточно интересные подходы к дизайну, которые круто смотрятся в дополненной и виртуальной реальности. Базовая идея основывается на объёме, свете, моушене и реалистичных материалах. По сути то, что Microsoft называет в своих интерфейсах акрилом мы и будем реализовывать. Подобное “мутное стекло” так же используется в айос и выглядит достаточно интересно. Один из примеров интересного применения в приложениях - это приложение New Yourk Times в разделе с дополненной реальности.

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

Данный акрил состоит по сути из:

  1. Blur/Размытие

  2. Color Burn/Затемнение основы

  3. Tint/Задание оттенка

  4. Noise/Шум

Для добавления объёма мы так же добавим в этот процесс Эффект Френеля. Так как он отлично имитирует нужный нам визуал, для того, чтобы у нашей модели были видны грани. Иначе данный акрил применим только для 2д интерфейсов, так как по самой технике не имеет объёма.

Blur или размытие

На первом же шаге, если подойти к нему неправильно и не знать нюансов платформ у нас возникнут проблемы. Во-первых, на мобильных платформах очень дорого использовать GrabPass, а во-вторых, если размывать мы будем достаточно большой кадр по алгоритму того же Гаусса у нас просядет производительность (просто будет убит филлрейт). Поэтому мы поступим хитрее. В целом блюр можно написать самостоятельно, но я пользуюсь модифицированным https://github.com/PavelDoGreat/Super-Blur Это очень крутой репозиторий с размытием обладающий двумя нужными нам функциями. Размытием полного кадра (оно дешевле чем грабпасс) и возможностью уменьшить разрешение полного кадра до нужного нам. Так как он всё равно буде сильно размыт, то нет необходимости в высоком качестве кадра, и можно взять его даунсемпл. В целом эти параметры можно в будущем крутить под конкретный эффект, платформу и доступный лимит ресурсов, чтобы получить разные материалы.

Сразу стоит сказать, что сам по себе акрил конечно в этом случае получится нечестный. Так как он либо будет учитывать сам себя, либо не будет учитывать другие акриловые объекты находящиеся за ним. Так как по сути в дальнейшем мы наше размытие пробрасываем в 3д модель и используем, как текстуру на модели. Для этого мы рассчитываем uv координаты исходя из позиции вершины на экране.

v2f vert (appdata_t v)
{
   v2f o;
   o.vertex = UnityObjectToClipPos(v.vertex);
   o.uv = ComputeScreenPos(o.vertex);
}
float4 frag(v2f i) : COLOR
{
   float4 col = tex2D(_MainTex, i.uv.xy / i.uv.w);
   return col;
}

Photoshop Blend Modes

По данной теме почти та же самая история. Их можно написать самостоятельно, но в целом существует уже готовый .cginc со всеми нужными блендмодами. https://github.com/penandlim/JL-s-Unity-Blend-Modes Правда для непрозрачных цветов. Для поддержки прозрачности нужно дорабатывать режимы смешивания самостоятельно.

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

Эффект Френеля

Так как у нас нет рефракции и по сути 3д модель выступает сейчас, как некая маска для того, чтобы показывать нам, что же скрыто за ней в размытой текстуре экрана - нам нужно как-то добавить объём. Можно в целом реализовать преломления, но они тут необязательны, так как с таким сильным размытием визуальная разница будет незаметна. По сути акрил - это отражающий материал, поэтому мы можем задать нужный нам объём Эффектом Френеля.

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

v2f vert (appdata_t v)
{
   v2f o;
   o.vertex = UnityObjectToClipPos(v.vertex);
   o.uv = ComputeScreenPos(o.vertex);
   //Freshel
   float3 i = normalize(ObjSpaceViewDir(v.vertex));
   o.fresnel = _FresnelBias + _FresnelScale * pow(1 + dot(i, v.normal), -_FresnelPower);
}
half4 frag(v2f i) : COLOR
{
   float4 col = tex2D(_MainTex, i.uv.xy / i.uv.w);
 
   //Color Burn
   col.xyz = LinearBurn(col.xyz, _BurnColor.xyz);

   //Freshnel
   col = lerp(col, 1 - _FresnelColor, 1 - i.fresnel);
   return col;
}

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

Шум

С шумом всё довольно просто. У нашей модели есть развёртка. Так как в uv один мы пишем параметры пересчитанные для вершинной части, то для передачи в фрагментную часть оригинальной развёртки можно использовать uv2 (или поменять их местами, тут кому как больше нравится) А дальше мы смешиваем шум по принципу того, что чёрный - это у нас альфа канал равный нулю, а в белый - альфа равная единице. И получаем нужное нам лёгкое “напыление на материале”. Собственно всё.

v2f vert (appdata_t v)
{
   v2f o;
   o.vertex = UnityObjectToClipPos(v.vertex);
   o.uv = ComputeScreenPos(o.vertex);
   o.uv2 = v.texcoord;
   float3 i = normalize(ObjSpaceViewDir(v.vertex));
  
   //Freshel
   o.fresnel = _FresnelBias + _FresnelScale * pow(1 + dot(i, v.normal), -_FresnelPower);
  
   return o;
}
half4 frag(v2f i) : COLOR
{
   float4 col = tex2D(_MainTex, i.uv.xy / i.uv.w);
 
   //Color Burn
   col.xyz = LinearBurn(col.xyz, _BurnColor.xyz);
  
   //Noise
   float4 noise = tex2D(_NoiseTex, i.uv2);
   col = lerp(col, noise, noise.r);

   //Freshnel
   col = lerp(col, 1 - _FresnelColor, 1 - i.fresnel);
   return col;
}

Tint или задание оттенка

Так как в Unity чаще всего Tint подразумевает умножение цветов, то таким образом его и сделаем. Просто умножим нужный нам цвет, на цвет полученный в шаге Color Burn.

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

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

Вот и тренировке конец

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

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

А в конце хочу предложить несколько простых упражнений (одно из них решено в репозитории, так что рекомендую не подглядывать, если захотите их про решать):

  1. Сделать возможность собрать на основе этого шейдера окно с выложенной мозаикой

  2. Сделать возможность красить 3д модели в разные цвета в один dc без использования текстурных атласов.

  3. Сделать шум не в виде текстуры, а рассчитываемым получив нужный мутный визуальный эффект.

Комментарии (0)