Как я могу сделать естественные капли дождя на экране?

11

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

https://www.shadertoy.com/view/ltffzl

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

введите описание изображения здесь

моя идея - использовать рендерер текстур и следов для отбрасывания, но как мне получить эффект метабола? введите описание изображения здесь


Обновить

Я мог бы реализовать метаболлы по этой статье

https://github.com/smkplus/RainFX/tree/master

но я понятия не имею о след

Сейед Мортеза Камали
источник

Ответы:

11

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

Разбивая то, что делает ваш пример с шадертой, он просто вычисляет «след», чтобы показать, где происходит очищение:

введите описание изображения здесь

Рассчитывает нормали круговых капель дождя,

введите описание изображения здесь

и использует эту карту нормалей для смещения текстурного поиска до искусственного преломления.

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

float trailDrop(vec2 uv, vec2 id, float t) { 
    // wobbly path
    float wobble = 0.5 + 0.5 
        * cos(120.0 * uv.y) 
        * sin(50.0 * uv.y);
    float v = 1.0 - 10.0 * abs(uv.x - 0.5 + 0.2 * wobble);
    // head
    v *= clamp(30.0 * uv.y, 0.0, 1.0);
    v *= clamp( uv.y + 7.0 * t - 0.6, 0.0, 1.0);
    // tail
    v *= clamp(1.0 - uv.y - pow(t, 2.0), 0.0, 1.0);
    return clamp(v * 10.0, 0.0, 1.0);
}

Вот грубый POC в шадертой - https://www.shadertoy.com/view/XlBfz1, демонстрирующий создание набора троп троп дождевой капли. Он выглядит зернистым при небольших разрешениях из-за разрешения производных, но должен выглядеть лучше, если вы его полноэкранный.

Изменить: Добавлен пример с наложенными каплями дождя

введите описание изображения здесь

Оставлено в качестве упражнения для читателя:

1) Добавьте маленькие круглые капли. Для вдохновения посмотрите на StaticDropsфункцию в вашем оригинальном примере с шадертой.

2) Добавьте в качественные нормальные расчеты. Как следует из #define CHEAP_NORMALSварианта в вашем исходном примере с шейдертой, встроенный dFdx является приближением низкой точности, и вы можете получить лучшие результаты, вручную вычисляя производные (за счет вычисления функции 3 раза).

3) Рандомизация расстояния между столбцами. Вы можете расширить столбцы, а затем изменить uv.x - 0.5 + 0.2 * wobbleбит, чтобы добавить случайное смещение по оси x. Вы также, вероятно, захотите еще раз вынуть страницу из исходного примера и наложить несколько слоев потоков разного размера друг на друга, чтобы получить менее однородный вид.

Джимми
источник
@DMGregory отметил. удаление комментария
Джимми
Сам след можно сделать через буфер, замирая (вернуть oldValue * .95 + newdiskposition). Обычно люди используют шум Перлина для возмущения прямой линии.
Сейед Мортеза Камали
что-то вроде этого shadertoy.com/view/4dy3zR Я пытался сделать шумный след, но я не мог
Сейед Мортеза Камали
7

Вы можете сделать этот эффект, выполнив следующие действия:

частица

частица

RenderTextuer

Вы можете сохранить результат с помощью RenderTexture. это пример многопроходности в шадертой:

https://www.shadertoy.com/view/ltccRl

Иньиго Квилез: Shadertoy использует несколько проходов, по одному на «Buffer». Как видно из названия, эти этапы сохраняют результаты в буфере. Буфер это просто текстура. Unity также позволит вам визуализировать текстуры.

Я создал камеру для рендеринга частиц в RenderTexture:

топор

RenderTexture

GrabPassing

Вы можете получить пропуск для применения искажения

Я объяснил это в этом посте:

Как я могу воспроизвести эффект искажения частиц Quantum Break?

пятно

с помощью альфа-цвета в течение жизни у нас есть простое размытие

alphaovertime

градиен

чтобы получить лучший результат, лучше использовать простое размытие, но как нам добиться размытия?

Сверточная матрица

При обработке изображений ядро, матрица свертки или маска представляют собой небольшую матрицу. Он используется для размытия, повышения резкости, тиснения, обнаружения краев и многого другого. Это достигается путем свертки между ядром и образом.

для более подробной информации, пожалуйста, перейдите по этой ссылке

ядро

 Shader "Smkgames/Convolution"
    {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            [Enum(kerEdgeDetectionA,1,kerEdgeDetectionB,2,kerEdgeDetectionC,3,kerSharpen,4,kerBoxBlur,5)]
            _Kernel("Kernel", Float) = 1
        }
        SubShader
        {
            // No culling or depth
            Cull Off ZWrite Off ZTest Always

            Pass
            {
                CGPROGRAM

                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };

                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                };

                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    return o;
                }

                sampler2D _MainTex;
                float4 _MainTex_TexelSize;

                float3x3 GetData(int channel, sampler2D tex, float2 uv, float4 size)
                {
                    float3x3 mat;
                    for (int y=-1; y<2; y++)
                    {  
                        for(int x=-1; x<2; x++)
                        {      
                            mat[x+1][y+1]=tex2D(tex, uv + float2(x*size.x, y*size.y))[channel];
                        }              
                    }
                    return mat;
                }
                float3x3 GetMean(float3x3 matr, float3x3 matg, float3x3 matb)
                {
                    float3x3 mat;
                    for (int y=0; y<3; y++)
                    {  
                        for(int x=0; x<3; x++)
                        {
                            mat[x][y] = (matr[x][y] + matg[x][y] + matb[x][y]) / 3.0;
                        }
                    }
                    return mat;
                }

                float Convolve(float3x3 kernel, float3x3 pixels, float denom, float offset)
                {
                    float res = 0.0;
                    for (int y=0; y<3; y++)
                    {  
                        for(int x=0; x<3; x++)
                        {
                            res += kernel[2-x][2-y]*pixels[x][y];
                        }
                    }

                    return  res;
                }

                float _Kernel;

                fixed4 frag (v2f i) : SV_Target
                {


                    float3x3 kerEdgeDetectionA = float3x3 (    0.0,  0,  -1.0,
                                                        1.0,  0,  -1.0,
                                                        0.0,  1.0,  0.0);

                   float3x3 kerEdgeDetectionB = float3x3 (0.0,  1.0,  0.0,
                                                 1.0, -4.0,  1.0,
                                                 0.0,  1.0, 0.0);

                   float3x3 kerEdgeDetectionC = float3x3 (-1.0, -1.0, -1.0,
                                                    -1.0,  8.0, -1.0,
                                                    -1.0, -1.0, -1.0);

                   float3x3 kerSharpen = float3x3 (0.0, -1.0, 0.0,
                                                    -1.0, 5.0, -1.0,
                                                    0.0, -1.0, 0.0);



                    float3x3 kerBoxBlur = (1.0/9.0)*float3x3 (    1.0,  1.0,  1.0,
                                                        1.0,  1.0,  1.0,
                                                        1.0,  1.0,  1.0);




                    float3x3 kernelSelection;
                if(_Kernel == 1){
                kernelSelection = kerEdgeDetectionA;
                }else if(_Kernel == 2){
                kernelSelection = kerEdgeDetectionB;    
                }else if(_Kernel == 3){
                kernelSelection = kerEdgeDetectionC;
                }else if(_Kernel == 4){
                kernelSelection = kerSharpen;   
                }else if(_Kernel == 5){
                kernelSelection = kerBoxBlur;
                }

                float3x3 matr = GetData(0, _MainTex, i.uv, _MainTex_TexelSize);
                float3x3 matg = GetData(1, _MainTex, i.uv, _MainTex_TexelSize);
                float3x3 matb = GetData(2, _MainTex, i.uv, _MainTex_TexelSize);
                float3x3 mata = GetMean(matr, matg, matb);


                // kernel
               float4 gl_FragColor = float4(Convolve(kernelSelection,matr,1.0,0.0),
                                            Convolve(kernelSelection,matg,1.0,0.0),
                                            Convolve(kernelSelection,matb,1.0,0.0),
                                            1.0);

                return gl_FragColor;
            }
            ENDCG
        }
    }
}

Boxblur

Рамка размытия (также известная как линейный фильтр) - это линейный фильтр в пространственной области, в котором каждый пиксель в результирующем изображении имеет значение, равное среднему значению его соседних пикселей во входном изображении. Это форма фильтра нижних частот («размытие»). Размытость 3 на 3 можно записать в виде матрицы

https://en.wikipedia.org/wiki/Box_blur

1_oos3y1ztoewgsubpdnbvea

Shader "Smkgames/Simple Box Blur"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Blend SrcAlpha OneMinusSrcAlpha


        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float4 _MainTex_TexelSize;

            float4 box(sampler2D tex, float2 uv, float4 size)
            {
                float4 c = tex2D(tex, uv + float2(-size.x, size.y)) + tex2D(tex, uv + float2(0, size.y)) + tex2D(tex, uv + float2(size.x, size.y)) +
                            tex2D(tex, uv + float2(-size.x, 0)) + tex2D(tex, uv + float2(0, 0)) + tex2D(tex, uv + float2(size.x, 0)) +
                            tex2D(tex, uv + float2(-size.x, -size.y)) + tex2D(tex, uv + float2(0, -size.y)) + tex2D(tex, uv + float2(size.x, -size.y));

                return c / 9;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col = box(_MainTex, i.uv, _MainTex_TexelSize);
                return col;
            }
            ENDCG
        }
    }
}

blurbox

Репетиция

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

0fe28c6167db2132d4bb8677fc1b2050 - Леандро-Эрлих-аргентина

Обычный

float4 distortion = tex2D(_MainTex,i.uv);
float3 distortionNormal = UnpackNormal(distortion);

record_2019_03_03_21_35_45_417

Вывод

Финальный шейдер:

Shader "Smkgames/BrokenGlass3D"
{
    Properties{
        _MainTex("MainTex",2D) = "white"{}
        _NormalIntensity("NormalIntensity",Float) = 1
        _Alpha("Alpha",Float) = 1
    }
    SubShader
    {
Tags {"Queue"="Transparent" "RenderType"="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha 


        GrabPass
        {
            "_GrabTexture"
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float2 grabPos : TEXCOORD1;
                float3 normal :NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 grabPos : TEXCOORD1;
                half3 worldNormal :TEXCOORD2;
                float4 vertex : SV_POSITION;

            };
            sampler2D _MainTex;
            float _Intensity,_Alpha;

            v2f vert (appdata v)
            {
                v2f o;
                o.uv = v.uv;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.grabPos = ComputeGrabScreenPos(o.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            sampler2D _GrabTexture;
            float _NormalIntensity;

            fixed4 frag (v2f i) : SV_Target
            {
                float4 distortion = tex2D(_MainTex,i.uv);
                float3 distortionNormal = UnpackNormal(distortion);
                distortionNormal.xy *= _NormalIntensity;
                normalize(distortionNormal);
                fixed4 col = tex2Dproj(_GrabTexture, i.grabPos+float4(distortionNormal.rgb,0));
                return col;
            }
            ENDCG
        }
    }
}

без использования альфа-цвета в течение жизни:

record_2019_03_03_21_48_36_273

используя альфа в цвете над Lifetime:

record_2019_03_03_21_48_19_786

Источник доступен:

https://github.com/smkplus/RainDrop

Есть больше!

также вы можете сделать рябь

record_2019_03_04_22_10_25_457

Полезные ссылки

https://80.lv/articles/breakdown-animated-raindrop-material-in-ue4/

https://seblagarde.wordpress.com/2013/01/03/water-drop-2b-dynamic-rain-and-its-effects/

Сейед Мортеза Камали
источник
1

На самом деле был вопрос об этом много лет назад, но он вообще не относится к Unity (к сожалению). Если вы посмотрите на слайд 57 связанной презентации, они упоминают подход на основе сетки.

На Physics SE есть несколько взаимосвязанный вопрос, который может показаться вам интересным. Ссылка на droplet.pdf в связанном вопросе не работает, но она все еще находится на Wayback Machine. Это входит в некоторую математику воды, бегущей с нескольких типов поверхностей. Например, капли предпочитают путешествовать по путям, ранее использовавшимся в предыдущих каплях дождя (см. С. 926).

Вы могли бы, вероятно, просто смоделировать головы и хвосты каждой «капли дождя» и позволить ей немного зигзагать. Когда сталкиваются два вытянутых капли дождя, я полагаю, вы могли бы объединить их в более крупную и быстро движущуюся каплю дождя. Объем воды остается прежним. Он просто перемещается и формируется силами гравитации, адгезии к стеклу и сцепления.

Дэвид А
источник