Як я можу зробити природні краплі дощу на екрані?


11

Я намагаюся зробити ефект дощу за допомогою метаболів та сліду на екрані. Я знаходжу підказку в шадертої, але я не розумів, як реалізовано:

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

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

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

моя ідея полягає у використанні текстури та контуру для відстеження, але як я можу мати ефект метаболів? введіть тут опис зображення


Оновлення

Я міг би реалізувати Metaballs відповідно до цієї статті

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

але я не маю ідеї про стежку

Відповіді:


11

Я думаю, що вам слід подумати про ефект, як "обчислити карту, де вода" + ", генерувати з цієї карти звичайний вектор і використовувати його для компенсації пошуку фонової текстури".

Розбиваючи, що робить ваш приклад shadertoy, він просто обчислює "слід", щоб показати, де відбувається розпушування:

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

Обчислює норми кругових крапель дощу,

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

і використовує цю звичайну карту для компенсації пошуку текстури для підробленого заломлення.

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

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 я спробував зробити галасливий слід , але я не міг
Сейєд Morteza Kamali

7

Ви можете зробити цей ефект, виконавши наведені нижче дії.

Частинка

Частинка

RenderTextuer

ви можете зберігати результат, використовуючи RenderTexture. це приклад мультипасу в шадертої:

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

iñigo quilez: Шадертой використовує кілька пропусків, один на "буфер". Як вказує ім'я, цей пропуск зберігає результати в буфері. Буфер - це лише текстура. Єдність дозволить вам також надавати текстури.

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

сокира

RenderTexture

GrabPassing

ви можете взяти пропуск для застосування Спотворення

Я пояснив це в цій публікації:

Як я можу повторити ефект частинок спотворення Quantum Break?

Розмиття

використовуючи кольори альфа упродовж життя, ми маємо просте розмиття

альфа-час

градієнт

щоб отримати кращий результат, краще використовувати просте розмиття, але як ми досягаємо розмиття?

Матриця згортки

При обробці зображення ядро, матриця згортки або маска - це невелика матриця. Застосовується для розмивання, різкості, тиснення, виявлення країв тощо. Це досягається шляхом згортання між ядром і зображенням.

Для отримання детальної інформації перейдіть за цим посиланням

Ядро

 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 для зберігання попереднього frame.so ви можете захопити попередній кадр, а потім розмити. повторюючи це, ви досягаєте розмиття.

0fe28c6167db2132d4bb8677fc1b2050 - leandro-erlich-argentina

Нормальний

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
        }
    }
}

без використання альфа-кольорів протягом усього життя:

запис_2019_03_03_21_48_36_273

використовуючи кольори альфа протягом усього життя:

запис_2019_03_03_21_48_19_786

Джерело доступне:

https://github.com/smkplus/RainDrop

Є ще більше!

також ви можете зробити брижі

запис_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

З цього питання насправді виникало питання , але це зовсім не стосується Єдності (на жаль). Якщо подивитися на слайд 57 зв'язаної презентації, вони згадують підхід на основі сітки.

Існує дещо пов’язане питання щодо фізики SE, яке вам може бути цікавим. Посилання на droplet.pdf у пов'язаному питанні порушено, але воно все ще знаходиться на машині Wayback. Це входить у частину математики води, що стікає з декількох типів поверхонь. Краплі вважають за краще подорожувати шляхами, якими раніше користувалися дощові краплі, наприклад (див. P926).

Ви, мабуть, могли просто моделювати голови та хвости кожної "краплі дощу" та дозволяти їй трохи зігнути та заграти. Коли стикаються дві витягнуті краплі дощу, я гадаю, ви могли б змусити їх об'єднатись у більшу і швидку краплину краплі дощу. Об'єм води залишається однаковим. Це просто переміщується та формується силами сили тяжіння, адгезії до скла та згуртованості.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.