Взрывная волна в Unity3D (displacement shader)

    Мой телеграм канал: https://t.me/winc0de.
    Всем привет, пишу небольшую 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 тени от физических объектов
    • +24
    • 24.8k
    • 9
    Share post

    Similar posts

    Comments 9

      0
      По шейдеру:
      1. Почему «Hidden»?

      2.
      v2f_vct vert (vin_vct v)
      ...
      o.color = v.color;
      

      Зачем?

      3. Не совсем понятно наличие высокой точности во фрагментном шейдере — достаточно даже fixed-ов, что хорошо увеличит скорость на полноэкранных эффектах.
        0
        По поводу первого, просто я этот материал применяю кодом в некоторых местах. Можно сделать Custom и цеплять на любой эффект по желанию.
        На счет 2 спасибо, там код со старых экспериментов был, можно убрать свою функцию обработки вершин.
        По поводу третьего, это уже дело оптимизации. Можно и качество render текстуры понижать для подобных эффектов и т.д.
          0
          По поводу первого

          Я про то, что нет опасения подмены одного из штатных шейдеров юнити сейчас / в будущем? Почему не более длинное имя с несколькими уровнями вложенности?

          дело оптимизации

          Если оно делалось как proof of concept, то можно было и все в шейдере считать, как было описано в самом начале. Вроде как основной идеей статьи было описание трика по оптимизации. А раз сказал А — говори и Б. :)

          render текстуры понижать для подобных эффектов

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

        Вы замеряли производительность на мобильном устройстве?
          0
          Не замерял, но сам процесс можно хорошо оптимизировать:
          1) Как указали выше, уменьшить точность
          2) Уменьшить качество рендер текстуры (делать её меньше, чем размер вьюпорта, убирать антиалиасинг, выбирать color-format с маленькой битностью на цвет).
          3) Стараться комбинировать эффекты, использующие рендер в текстуру в один проход по возможности.
          0
          Если вам интересна эта тема
          <...>
          — Гравитационные искажения (физическая часть и визуальная)

          Да! Давно мечтал попробовать эффекты общей теории относительности в каком-нибудь симуляторе, но в Universe Sandbox, кажется, пока такого нет :)

          Будут ли чёрные дыры и гравитационное линзирование? ^_^
          Simulated view of a black hole in front of the Large Magellanic Cloud - Wiki
            0
            Здесь главное вписать подобное в геймплей :)
            Конкретно сейчас гравитационные искажения ускоряют или замедляют течение всех процессов (физических и визуальных) в зависимости от силы искажения пространств :)
            +1
            Да, тема интересна — продолжайте!

            Only users with full accounts can post comments. Log in, please.