Ефект кофти на шейдери для мобільних пристроїв

Пролог
Доброго часу доби! Після опублікування статті про візуалізації квадратичного дерева(Quad-tree), мене попросили написати статтю, що показує роботу шейдера, що переводить зображення у «кофту».



Так що, давай розглянемо цю методику.

Реалізація
Для реалізації алгоритму потрібно текстура зі стібками кофти. Вона виглядає наступним чином.



Текстура складається з 30 стібків однакової ширини в 30 пікселів. У підсумку, ми повинні виводити цю текстуру на екран, мене колір стібка під потрібний колір на екрані, і перемішувати самі стібки на екрані, щоб зображення не здавалося штучним.

Спочатку потрібно підготувати параметри для шейдера, мовою описаним у статті буде C# і GLSL відповідно.

override public void OnScaleUpdated(float scale) {
Vector2 screenSize = ScreenTool.GetViewportSize ();
Texture2D stitchTexture = textureLoader.texture;

float resolutionH = Mathf.Clamp((Mathf.RoundToInt(screenSize.x/(float)itemWidth * scale) * 2), 1, 2048);
float resolutionV = Mathf.Clamp((Mathf.RoundToInt(screenSize.y/(float)stitchTexture.height * scale) * 2), 1, 2048);
resolution = new Vector4 (resolutionH, resolutionV, 0f, 0f);

float amountH = (float)(resolutionH * itemWidth) / (2.0 f * (float)stitchTexture.width);
float amountV = (float)resolutionV / 2.0 f;
Vector4 amount = new Vector4 (amountH, amountV, 0f, 0f);

material.SetTexture ("_ItemTex", stitchTexture);
material.SetVector ("_Resolution", resolution);
material.SetVector ("_ItemsAmount", amount);
}


Функція викликається при оновленні коефіцієнта скалирования, в ній розраховується кількість текстур з стібками.
  • Amount — це кількість текстур, яке влізе на екран;
  • itemWidth — ширина одного стяжка;


У змінній amountH відбувається ділення на 2, це зроблено для того, щоб розділити один стібок по полам і змінювати колір половинки стібка.

Перейдемо до реалізації шейдера.
На шейдер буде передаватися зменшене зображення з камери, для того щоб розбити зображення на блоки, які в наступному будуть перетворені у стібки.



Спочатку потрібно визначити в якому блоці ми зараз знаходимося. Блоки утворені тим, що наше зменшене зображення потім виявляється розтягнутим до повного розміру, і кожен піксель утворює блок пікселів одного кольору.

float2 colorBlock = floor(input.tex * _Resolution);


Наступний етап для мене виявився найскладнішим, необхідно на певний блок, щодо вибрати випадковий стібок з текстури.
float2 stichUV = float2(frac(_ItemsAmount.x * (input.tex.x + pow(colorBlock.y 2.0) / _Resolution.x*2)), frac(_ItemsAmount.y * input.tex.y));


Далі беремо колір блоку, колір стібка і колір попереднього блоку. Колір попереднього блоку необхідно брати, так як стібки виходять за межі своїх блоків.
fixed4 color = tex2D(_MainTex, input.tex);
fixed4 newColor = tex2D(_ItemTex, stichUV);
fixed4 prevColor = tex2D(_MainTex, float2(input.tex.x, input.tex.y + 1.0/_Resolution.y));




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

if (stichUV.y > 0.5)
{
newColor *= color;
float2 topStichUV = frac(float2(_ItemsAmount.x * (input.tex.x + pow(colorBlock.y + 1, 2) / _Resolution.x*2), _ItemsAmount.y*input.tex.y - 0.5));
fixed4 otherStich = tex2D(_ItemTex, topStichUV) * prevColor;

if (otherStich.a > 0.05)
{
newColor = lerp(newColor, otherStich, 1 - newColor.a);
}
}


Спочатку змішуємо колір стібка з кольором блоку, після чого знаходимо стібок, який повинен отрисовываться над червоною частиною і потім змішуємо дві частини стібка.

Нижня частина розмальовує аналогічно (зелена):
else
{
stichUV = float2(frac(_ItemsAmount.x * (input.tex.x + pow(colorBlock.y + 1, 2) / _Resolution.x*2)), frac(_ItemsAmount.y*input.tex.y));
newColor = tex2D(_ItemTex, stichUV);
newColor *= prevColor;
float2 bottomStichUV = float2(frac(_ItemsAmount.x * (input.tex.x + pow(colorBlock.y, 2) / _Resolution.x*2)), frac(_ItemsAmount.y*input.tex.y + 0.5));
fixed4 otherStich = tex2D(_ItemTex, bottomStichUV) * color;

if (otherStich.a > 0.7)
{
newColor = lerp(otherStich, newColor, 1 - prevColor.a);
}
}


Епілог
В результаті отримуємо досить шустрий шейдер, який при бажанні можна змусити добре працювати на старих Android (на iOS він вже буде працювати нормально). При цьому можна використовувати різні текстури для зміни ефекту, припустимо вишивка хрестиком.

Спасибі за увагу!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.