Wie kann ich natürliche Regentropfen auf dem Bildschirm erzeugen?


11

Ich versuche , regen Effekt Tropfen mit machen metaballs und Karte auf screen.I finden einen Anhaltspunkt in shadertoy aber ich verstand nicht, wie implementiert:

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

Leider hat es viele mathematische Berechnungen und ich kann es nicht in Einheit verwenden, weil es eine Verzögerung erzeugt. Natürlich sollte ich Texturen verwenden, aber wie kann ich einen Trail-Effekt haben?!

Geben Sie hier die Bildbeschreibung ein

Meine Idee ist es, einen Textur- und Trail-Renderer zum Löschen zu verwenden, aber wie kann ich Metaballs-Effekte erzielen? Geben Sie hier die Bildbeschreibung ein


Aktualisieren

Ich könnte Metaballs durch diesen Artikel implementieren

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

aber ich habe keine Ahnung von Spur

Antworten:


11

Ich denke, Sie sollten über den Effekt nachdenken, indem Sie "eine Karte berechnen, auf der sich das Wasser befindet" + "einen normalen Vektor aus dieser Karte generieren und damit eine Hintergrundtextur-Suche versetzen".

Wenn Sie die Funktionsweise Ihres Beispiel-Shadertoys aufschlüsseln, wird lediglich eine "Spur" berechnet, um zu zeigen, wo das Entnebeln stattfindet:

Geben Sie hier die Bildbeschreibung ein

Berechnet die Normalen der kreisförmigen Regentropfen,

Geben Sie hier die Bildbeschreibung ein

und verwendet diese normale Karte, um eine Textur-Suche zu einer falschen Brechung zu versetzen.

Wenn Sie möchten, dass die Trails in einem Shader nachbearbeitet werden, sollten Sie einfach die "Trail" -Form im Shader mit etwas Algebra erstellen. Zum Beispiel habe ich in der folgenden Funktion einen "wackeligen Pfad" und eine Verjüngung an Kopf und Schwanz überlagert, um sie zu erhalten

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

Hier ist ein grober POC in Shadertoy - https://www.shadertoy.com/view/XlBfz1 , der das Erstellen einer Reihe von Regentropfen zeigt. Bei kleinen Auflösungen sieht es aufgrund der Auflösung der Derivate körnig aus, sollte aber besser aussehen, wenn Sie es im Vollbildmodus anzeigen.

Bearbeiten: Ein Beispiel mit überlagerten Regentropfen wurde hinzugefügt

Geben Sie hier die Bildbeschreibung ein

Als Übung dem Leser überlassen:

1) Fügen Sie die kleinen runden Tropfen hinzu. Schauen Sie sich zur Inspiration die StaticDropsFunktion in Ihrem ursprünglichen Shadertoy-Beispiel an.

2) Fügen Sie hochwertige Normalberechnungen hinzu. Wie die #define CHEAP_NORMALSOption in Ihrem ursprünglichen Shadertoy-Beispiel impliziert, handelt es sich bei dem integrierten dFdx um eine Low-Fidelity-Näherung, und Sie können bessere Ergebnisse erzielen, indem Sie die Ableitungen manuell berechnen (auf Kosten der dreifachen Berechnung der Funktion).

3) Randomisierung des Abstands zwischen den Spalten. Sie können die Spalten erweitern und dann das uv.x - 0.5 + 0.2 * wobbleBit ändern , um einen zufälligen Versatz auf der x-Achse hinzuzufügen. Wahrscheinlich möchten Sie auch noch einmal eine Seite aus dem Originalbeispiel herausnehmen und ein paar unterschiedlich große Ebenen von Streams übereinander legen, um ein weniger einheitliches Erscheinungsbild zu erhalten.



@ DMGregory Noted. Metaball Kommentar entfernen
Jimmy

Der Trail selbst kann über einen Puffer durch Ausblenden erstellt werden (return oldValue * .95 + newdiskposition). Normalerweise verwenden Leute Perlin-Rauschen, um eine gerade Linie zu perburbieren.
Seyed Morteza Kamali

so etwas wie dieses shadertoy.com/view/4dy3zR Ich habe versucht, eine laute Spur zu machen, aber ich konnte nicht
Seyed Morteza Kamali

7

Sie können diesen Effekt erzielen, indem Sie die folgenden Schritte ausführen:

Partikel

Partikel

RenderTextuer

Sie können das Ergebnis mithilfe von RenderTexture speichern. Dies ist ein Beispiel für Multipass in Shadertoy:

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

iñigo quilez: Shadertoy verwendet mehrere Durchgänge, einen pro "Puffer". Wie der Name schon sagt, speichern diese Durchgänge die Ergebnisse in einem Puffer. Ein Puffer ist nur eine Textur. Mit Unity können Sie auch Texturen rendern.

Ich habe eine Kamera zum Rendern von Partikeln in RenderTexture erstellt:

Axt

RenderTexture

GrabPassing

Sie können sich einen Pass zum Anwenden von Distortion schnappen

Ich habe es in diesem Beitrag erklärt:

Wie kann ich den Verzerrungsteilcheneffekt von Quantum Break replizieren?

Verwischen

Durch die Verwendung von Alpha in Farbe über die Lebensdauer hinweg erhalten wir eine einfache Unschärfe

Alphaovertime

gradant

Um ein besseres Ergebnis zu erzielen, ist es besser, einfache Unschärfe zu verwenden, aber wie erreichen wir Unschärfe?

Faltungsmatrix

Bei der Bildverarbeitung ist ein Kernel, eine Faltungsmatrix oder eine Maske eine kleine Matrix. Es wird zum Verwischen, Schärfen, Prägen, Kantenerkennen und mehr verwendet. Dies wird erreicht, indem eine Faltung zwischen einem Kernel und einem Image durchgeführt wird.

Für weitere Informationen folgen Sie bitte diesem Link

Kernel

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

Box Unschärfe

Eine Box-Unschärfe (auch als Box-Linearfilter bekannt) ist ein räumlicher linearer Domänenfilter, bei dem jedes Pixel im resultierenden Bild einen Wert hat, der dem Durchschnittswert seiner benachbarten Pixel im Eingabebild entspricht. Es ist eine Form des Tiefpassfilters ("Unschärfe"). Eine 3 x 3-Box-Unschärfe kann als Matrix geschrieben werden

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

Unschärfebox

Wiederholung

Sie können Rendertexture verwenden, um den vorherigen Frame zu speichern. Sie können also den vorherigen Frame greifen und dann verwischen. Wenn Sie dies wiederholen, erhalten Sie Unschärfe.

0fe28c6167db2132d4bb8677fc1b2050 - leandro-erlich-argentina

Normal

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

record_2019_03_03_21_35_45_417

Fazit

Letzter Shader:

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

ohne Verwendung von Alpha in Farbe über die Lebensdauer:

record_2019_03_03_21_48_36_273

durch Verwendung von Alpha in Farbe über die Lebensdauer:

record_2019_03_03_21_48_19_786

Quelle ist verfügbar:

https://github.com/smkplus/RainDrop

Es gibt mehr!

Sie können auch Wellen machen

record_2019_03_04_22_10_25_457

Nützliche Links

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

Es gab tatsächlich eine FrageVor Jahren , die sich jedoch (leider) überhaupt nicht auf Unity bezieht. Wenn Sie sich Folie 57 der verknüpften Präsentation ansehen , wird ein gitterbasierter Ansatz erwähnt.

Es gibt eine etwas verwandte Frage zu Physics SE, die Sie vielleicht interessant finden. Der Link zu droplet.pdf in der verknüpften Frage ist unterbrochen, befindet sich jedoch immer noch auf der Wayback-Maschine. Es geht um einen Teil der Mathematik des Wassers, das von einigen Arten von Oberflächen abläuft. Tröpfchen ziehen es beispielsweise vor, auf Pfaden zu wandern, die zuvor von früheren Regentropfen verwendet wurden (siehe Seite 926).

Sie könnten wahrscheinlich nur die Köpfe und Schwänze jedes "Regentropfens" modellieren und es ein bisschen zick und zack lassen. Wenn zwei längliche Regentropfen kollidieren, könnten sie sich zu einem größeren und sich schneller bewegenden Regentropfen verbinden. Das Wasservolumen bleibt gleich. Es wird nur durch die Schwerkraft, die Haftung am Glas und den Zusammenhalt bewegt und geformt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.