Comment faire des gouttes de pluie naturelles à l'écran?

11

J'essaie de faire tomber l'effet de pluie avec des métaboules et des traces à l'écran.Je trouve un indice dans shadertoy mais je n'ai pas compris comment implémenté:

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

malheureusement, il a beaucoup de calculs mathématiques et je ne peux pas l'utiliser à l'unité car il produit un décalage.Bien sûr, je devrais utiliser des textures mais comment puis-je avoir un effet de traînée?!

entrez la description de l'image ici

mon idée utilise un rendu de texture et de traînée pour le drop mais comment puis-je avoir un effet de métabilles entrez la description de l'image ici


Mise à jour

Je pourrais implémenter des métaballes par cet article

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

mais je n'ai aucune idée du sentier

Seyed Morteza Kamali
la source

Réponses:

11

Je pense que vous devriez penser à l'effet comme "calculer une carte de l'endroit où se trouve l'eau" + "générer un vecteur normal à partir de cette carte et l'utiliser pour compenser une recherche de texture d'arrière-plan".

En décomposant ce que fait votre exemple de shadertoy, il calcule simplement une "trace" pour montrer où le désembuage se produit:

entrez la description de l'image ici

Calcule les normales des gouttes de pluie circulaires,

entrez la description de l'image ici

et utilise cette carte normale pour compenser une recherche de texture pour une fausse réfraction.

Si vous voulez que les traces soient effectuées via le post-traitement dans un shader, vous devez simplement créer la forme "trail" dans le shader en utilisant une algèbre. Par exemple, dans la fonction suivante, j'ai superposé un "chemin bancal" et un cône à la tête et à la queue pour obtenir

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

Voici un POC approximatif dans shadertoy - https://www.shadertoy.com/view/XlBfz1 démontrant la création d'un ensemble de traînées de gouttes de pluie. Il semble granuleux à de petites résolutions en raison de la résolution des dérivés, mais devrait mieux paraître si vous le faites en plein écran.

Edit: Ajout d'un exemple avec des gouttes de pluie superposées

entrez la description de l'image ici

Laissé en exercice au lecteur:

1) Ajoutez les petites gouttes rondes. pour l'inspiration, regardez leStaticDrops fonction dans votre exemple original de shadertoy.

2) Ajoutez des calculs normaux de haute qualité. Comme le#define CHEAP_NORMALS option de votre exemple original de shadertoy l'indique, le dFdx intégré est une approximation basse fidélité et vous pouvez obtenir de meilleurs résultats en calculant manuellement les dérivées (au prix du calcul de la fonction 3 fois).

3) Randomiser l'espacement entre les colonnes. Vous pouvez élargir les colonnes puis modifier le uv.x - 0.5 + 0.2 * wobblebit pour ajouter un décalage aléatoire sur l'axe x. Vous souhaiterez probablement également retirer une page de l'exemple d'origine et superposer plusieurs couches de flux de tailles différentes les unes sur les autres pour obtenir un aspect moins uniforme.

Jimmy
la source
Les "métaboules" ne doivent pas nécessairement être de la géométrie. La version 2D de l'effet peut être aussi simple qu'une image formée d'une somme de fonctions d'atténuation radiales avec un seuil .
DMGregory
@DMGregory Noté. suppression du commentaire metaball
Jimmy
La piste elle-même peut être effectuée via un tampon, par fondu (retour oldValue * .95 + newdiskposition). Habituellement, les gens utilisent le bruit Perlin pour anéantir une ligne droite.
Seyed Morteza Kamali
quelque chose comme ça shadertoy.com/view/4dy3zR J'ai essayé de faire une piste bruyante mais je n'ai pas pu
Seyed Morteza Kamali
7

vous pouvez créer cet effet en suivant les étapes ci-dessous:

Particule

Particule

RenderTextuer

vous pouvez stocker le résultat en utilisant RenderTexture. c'est un exemple de multipass dans shadertoy:

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

iñigo quilez: Shadertoy utilise plusieurs passes, une par "tampon". Comme son nom l'indique, cette passe stocke les résultats dans un tampon. Un tampon n'est qu'une texture. Unity vous permettra également d'effectuer le rendu des textures.

J'ai créé une caméra pour rendre des particules à RenderTexture:

hache

RenderTexture

GrabPassing

vous pouvez saisir un laissez-passer pour appliquer la distorsion

Je l'ai expliqué dans ce post:

Comment reproduire l'effet de particules de distorsion de Quantum Break?

Brouiller

en utilisant alpha en couleur sur la durée de vie, nous avons un simple flou

alphaovertime

dégradé

pour obtenir de meilleurs résultats, il est préférable d'utiliser un flou simple, mais comment obtenir un flou?

Matrice de convolution

Dans le traitement d'image, un noyau, une matrice de convolution ou un masque est une petite matrice. Il est utilisé pour le flou, la netteté, le gaufrage, la détection des bords, etc. Ceci est accompli en faisant une convolution entre un noyau et une image.

pour plus de détails, veuillez suivre ce lien

Noyau

 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 flou de boîte (également connu sous le nom de filtre linéaire de boîte) est un filtre linéaire de domaine spatial dans lequel chaque pixel de l'image résultante a une valeur égale à la valeur moyenne de ses pixels voisins dans l'image d'entrée. Il s'agit d'une forme de filtre passe-bas ("flou"). Un flou de 3 par 3 peut être écrit sous forme de 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

Répétition

vous pouvez utiliser Rendertexture pour stocker l'image précédente. vous pouvez donc saisir l'image précédente, puis la rendre floue. en répétant cela, vous obtenez un flou.

0fe28c6167db2132d4bb8677fc1b2050 - leandro-erlich-argentine

Ordinaire

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

record_2019_03_03_21_35_45_417

Conclusion

Shader final:

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

sans utiliser alpha en couleur sur la durée de vie:

record_2019_03_03_21_48_36_273

en utilisant alpha en couleur sur la durée de vie:

record_2019_03_03_21_48_19_786

La source est disponible:

https://github.com/smkplus/RainDrop

Il y a plus!

vous pouvez aussi faire des ondulations

record_2019_03_04_22_10_25_457

Liens utiles

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/

Seyed Morteza Kamali
la source
1

Il y avait en fait une question à ce sujet il y a quelques années, mais elle ne concerne pas du tout (malheureusement). Si vous regardez la diapositive 57 de la présentation liée, ils mentionnent une approche basée sur une grille.

Il y a une question quelque peu connexe sur Physics SE qui pourrait vous intéresser. Le lien vers droplet.pdf dans la question liée est rompu, mais il est toujours sur la Wayback Machine. Il entre dans certaines des mathématiques de l'eau s'écoulant sur quelques types de surfaces. Les gouttelettes préfèrent se déplacer dans les chemins précédemment utilisés par les gouttes de pluie précédentes, par exemple (voir p926).

Vous pourriez probablement modéliser les têtes et les queues de chaque "goutte de pluie" et lui permettre de zigzaguer et de zaguer un peu. Lorsque deux gouttes de pluie allongées entrent en collision, je suppose que vous pourriez les combiner en une goutte de pluie plus grande et plus rapide. Le volume d'eau reste le même. Il est simplement déplacé et façonné par les forces de gravité, l'adhésion au verre et la cohésion.

David A
la source