Вибухова хвиля в Unity3D (displacement shader)

Всім привіт, пишу невелику 2D гру і паралельно хотів би розповідати про реалізацію деяких речей у Unity3D.
Програмування графіки саме по собі дуже цікаве заняття з безмежною кількістю варіацій результату. У цій статті опишу реалізацію викривлення простору від вибухової хвилі.


Зазвичай будь-які спотворення у площинах x,y роблять за допомогою карти нормалей, яка в компоненті квітів зберігає величину спотворень. За допомогою цього реалізовують поверхню води, вибухові хвилі, гаряче повітря і т. д.

У грі, яку пишу дуже багато пострілів, у зв'язку з цим кількість спотворень теж велике:



Один з варіантів: математична модель спотворення. Ми прямо в шейдере вважаємо за формулами спотворення в конкретній точці. Мінусом такого підходу є зайве навантаження. Адже всі ці прорахунки будуть виконуватися для кожного пікселів, тому в програмуванні графіки часто використовують текстури, всередині яких заздалегідь прораховані математичні значення чого-небудь. У випадку з вибуховою хвилею я взяв таку структуру:


Створюємо в Unity новий шейдер:

Shader "Hidden/DisplacementEffect"
{
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_DisplacementTex ("Displacement rt", 2D) = "white" {}
_DisplacementPower ("Displacement power", Float) = 0.025
}

SubShader {
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag

#include "UnityCG.cginc"

uniform sampler2D _MainTex;
uniform sampler2D _DisplacementTex;
uniform float _DisplacementPower;

float4 frag(v2f_img i) : COLOR
{
fixed4 displacementVector = tex2D(_DisplacementTex, i.uv);
fixed2 uv_distorted = i.uv + _DisplacementPower * displacementVector.xy;

return tex2D(_MainTex, uv_distorted);
}
ENDCG
}
}
}


На вхід два параметри: _DisplacementTex та _DisplacementPower. Перша і є наша текстура спотворення, а друге — сила спотворення.

Отримуємо силу спотворення в поточному пікселі:
float4 displacementVector = tex2D(_DisplacementTex, i.uvgrab);


Зміщуємо uv-координати поточного пікселя з зазначеної силою і напрямком, отриманим вище.
float2 uv_distorted = i.uvgrab + _DisplacementPower * displacementVector.xy;
return tex2D(_MainTex, uv_distorted);


Іншими словами, кожен піксель в displacement текстурі зберігає в кольорі вектор, на який ми повинні зрушити піксель зображення.

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

У момент вибуху ви створюєте на сцені в точці вибуху спрайт displacement текстури, який поміщається в шар displacements. Цей шар бачить тільки його окрема камера, яка рендерить в іншу текстуру.


На скріні вище Camera Preview показує те, що «бачить» камера шару displacements, а внизу вже ігровий результат, куди шар displacements не потрапляє, але вже видно результат обробки шейдером.

У шейдер, який ми вже написали вище параметр _DisplacementTex потрапить приблизно така текстура (при множинних пострілах):


По суті, чорний колір текстури це нуль, в цьому місці кольору результуючої текстури не зсуваються. Там же, де промальована текстура спотворення кольору інші. Як результат, будь-яку кількість вибухів і спотворень малюються рівно за два проходи:
  • Перший прохід це рендер окремої камери шару displacements всіх спотворень в одну текстуру. Матеріал текстури для всіх вибухів один, тому й прохід
  • Другий прохід, це ефект поверх сцени, який на вхід отримує рендер-текстуру спотворень і за один раз зрушує всі пікселі у всіх точках вибухів
Якщо вам цікава ця тема, продовжу описувати ефекти, які ви бачите на фото:

— Old TV, RGB Shift та інші постпроцесс ефекти
— Гравітаційні спотворення (фізична частина і візуальна)
— Унікальний патерн фону з використанням Wang Tiles
— 2D тіні від фізичних об'єктів

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

0 коментарів

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