Come posso fare gocce di pioggia naturali sullo schermo?


11

Sto cercando di far cadere l'effetto pioggia con metaballs e trail sullo schermo. Trovo un indizio in shadertoy ma non ho capito come implementato:

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

sfortunatamente ha molti calcoli matematici e non posso usarlo in unità perché produce un ritardo. Ovviamente dovrei usare le trame ma come posso avere l'effetto trail ?!

inserisci qui la descrizione dell'immagine

la mia idea è quella di utilizzare un renderizzatore di trame e tracce per l'abbandono, ma come posso avere l'effetto metaballs? inserisci qui la descrizione dell'immagine


Aggiornare

Potrei implementare Metaballs con questo articolo

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

ma non ho idea di trail

Risposte:


11

Penso che dovresti pensare all'effetto come "calcola una mappa di dove è l'acqua" + "genera un vettore normale da quella mappa e usalo per compensare una ricerca di texture di sfondo".

Abbattendo ciò che fa il tuo esempio shadertoy, calcola solo una "scia" per mostrare dove si verifica il defogging:

inserisci qui la descrizione dell'immagine

Calcola le normali delle gocce di pioggia circolari,

inserisci qui la descrizione dell'immagine

e utilizza quella normale mappa per compensare una ricerca di texture con rifrazione falsa.

Se vuoi che le tracce vengano eseguite tramite post-elaborazione in uno shader, devi semplicemente creare la forma "trail" nello shader usando un po 'di algebra. Ad esempio, nella seguente funzione, ho sovrapposto un "percorso traballante" e una conicità in testa e coda per ottenere

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

Ecco un POC approssimativo in shadertoy - https://www.shadertoy.com/view/XlBfz1 che dimostra la creazione di una serie di percorsi di gocce di pioggia. Sembra granuloso a piccole risoluzioni a causa della risoluzione dei derivati ​​ma dovrebbe apparire migliore se lo si visualizza a schermo intero.

Modifica: aggiunto un esempio con gocce di pioggia sovrapposte

inserisci qui la descrizione dell'immagine

Lasciato come esercizio al lettore:

1) Aggiungi le piccole gocce rotonde. per ispirazione, guarda la StaticDropsfunzione nel tuo esempio shadertoy originale.

2) Aggiungi calcoli normali di alta qualità. Come l' #define CHEAP_NORMALSopzione nell'esempio shadertoy originale implica, il dFdx incorporato è un'approssimazione a bassa fedeltà e puoi ottenere risultati migliori calcolando manualmente le derivate (al costo di calcolare la funzione 3 volte).

3) Randomizzare la spaziatura tra le colonne. È possibile allargare le colonne e quindi modificare il uv.x - 0.5 + 0.2 * wobblebit per aggiungere un offset casuale sull'asse x. Probabilmente vorrai anche estrarre di nuovo una pagina dall'esempio originale e sovrapporre un paio di strati di flussi di dimensioni diverse l'uno sopra l'altro per ottenere un aspetto meno uniforme.



@DMGregory Noted. rimuovendo il commento di metaball
Jimmy il

La traccia stessa può essere eseguita tramite un buffer, sbiadendo (return oldValue * .95 + newdiskposition). Solitamente le persone usano il rumore di Perlin per creare una linea retta.
Seyed Morteza Kamali,

qualcosa del genere shadertoy.com/view/4dy3zR Ho provato a fare un percorso rumoroso ma non ci sono riuscito
Seyed Morteza Kamali

7

puoi fare questo effetto seguendo i passaggi seguenti:

particella

particella

RenderTextuer

è possibile memorizzare i risultati utilizzando RenderTexture. questo è un esempio di multipass in shadertoy:

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

iñigo quilez: Shadertoy usa più passaggi, uno per "Buffer". Come indica il nome, questo passaggio memorizza i risultati in un buffer. Un buffer è solo una trama. Unity ti permetterà di renderizzare anche le trame.

Ho creato una videocamera per il rendering delle particelle in RenderTexture:

ascia

RenderTexture

GrabPassing

puoi prendere il pass per applicare la distorsione

L'ho spiegato in questo post:

Come posso replicare l'effetto particella di distorsione di Quantum Break?

blur

usando l'alfa a colori nel corso della vita abbiamo una sfocatura semplice

alphaovertime

gradiant

per ottenere risultati migliori è meglio usare la sfocatura semplice, ma come possiamo ottenere la sfocatura?

Matrice di convoluzione

Nell'elaborazione delle immagini, un kernel, una matrice di convoluzione o una maschera è una piccola matrice. Viene utilizzato per sfocatura, nitidezza, goffratura, rilevamento dei bordi e altro ancora. Questo si ottiene facendo una convoluzione tra un kernel e un'immagine.

per maggiori dettagli, segui questo link

nocciolo

 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

Un riquadro di sfocatura (noto anche come filtro lineare a riquadro) è un filtro lineare a dominio spaziale in cui ogni pixel nell'immagine risultante ha un valore uguale al valore medio dei pixel adiacenti nell'immagine di input. È una forma di filtro passa-basso ("sfocatura"). Una sfocatura 3 per 3 può essere scritta come matrice

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

Ripetizione

puoi usare Rendertexture per memorizzare il fotogramma precedente, quindi puoi prendere il fotogramma precedente e sfocare. ripetendo questo si ottiene sfocatura.

0fe28c6167db2132d4bb8677fc1b2050 - Leandro Erlich-argentina-

Normale

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

record_2019_03_03_21_35_45_417

Conclusione

Shader finale:

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

senza usare l'alfa a colori nel corso della vita:

record_2019_03_03_21_48_36_273

usando l'alfa a colori nel corso della vita:

record_2019_03_03_21_48_19_786

La fonte è disponibile:

https://github.com/smkplus/RainDrop

C'è più!

anche tu puoi fare increspature

record_2019_03_04_22_10_25_457

link utili

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

In realtà c'era una domanda su questo anni fa, ma non riguarda affatto Unity (purtroppo). Se guardi la diapositiva 57 della presentazione collegata, menzionano un approccio basato sulla griglia.

C'è una domanda in qualche modo correlata su Physics SE che potresti trovare interessante. Il collegamento a droplet.pdf nella domanda collegata è interrotto, ma è ancora sulla Wayback Machine. Entra in una parte della matematica dell'acqua che scorre su alcuni tipi di superfici. Le goccioline preferiscono viaggiare su percorsi precedentemente utilizzati da precedenti gocce di pioggia, ad esempio (vedi p926).

Probabilmente potresti semplicemente modellare le teste e le code di ogni "goccia di pioggia" e permettergli di zig e zag un po '. Quando due gocce di pioggia allungate si scontrano, suppongo che potresti farle combinare in una goccia di pioggia più grande e più veloce. Il volume di acqua rimane lo stesso. Viene semplicemente spostato e modellato dalle forze di gravità, adesione al vetro e coesione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.