Como posso fazer gotas de chuva natural na tela?

11

Estou tentando fazer com que o efeito da chuva caia com metaballs e rastreie na tela.

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

infelizmente ele tem muitos cálculos matemáticos e eu não posso usá-lo em unidade porque produz um atraso. obviamente devo usar texturas, mas como posso ter efeito de trilha ?!

insira a descrição da imagem aqui

minha idéia é usar um renderizador de textura e trilha para soltar, mas como posso ter efeito metaballs? insira a descrição da imagem aqui


Atualizar

Eu poderia implementar Metaballs por este artigo

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

mas eu não tenho idéia sobre trilha

Seyed Morteza Kamali
fonte

Respostas:

11

Eu acho que você deve pensar no efeito como "calcular um mapa de onde a água está" + "gerar um vetor normal desse mapa e usá-lo para compensar uma pesquisa de textura de fundo".

Quebrando o que o seu exemplo shadertoy faz, ele apenas calcula uma "trilha" para mostrar onde ocorre a desembaçamento:

insira a descrição da imagem aqui

Calcula as normais das gotas de chuva circulares,

insira a descrição da imagem aqui

e usa esse mapa normal para compensar uma pesquisa de textura para refração falsa.

Se você deseja que as trilhas sejam feitas via pós-processamento em um sombreador, basta criar a forma "trilha" no sombreador usando alguma álgebra. Por exemplo, na função a seguir, sobreponha um "caminho instável" e um atarraxamento na cabeça e na cauda para obter

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

Aqui está um POC aproximado em shadertoy - https://www.shadertoy.com/view/XlBfz1 demonstrando a criação de um conjunto de trilhas de chuva. Parece granulado em pequenas resoluções devido à resolução de derivativos, mas deve parecer melhor se você a exibir em tela cheia.

Editar: Adicionado um exemplo com gotas de chuva sobrepostas

insira a descrição da imagem aqui

Deixado como um exercício para o leitor:

1) Adicione as pequenas gotas redondas. para inspiração, veja a StaticDropsfunção no seu exemplo shadertoy original.

2) Adicione cálculos normais de alta qualidade. Como #define CHEAP_NORMALSimplica a opção no seu exemplo shadertoy original, o dFdx interno é uma aproximação de baixa fidelidade e você pode obter melhores resultados calculando manualmente as derivadas (ao custo de calcular a função 3 vezes).

3) Aleatorizar o espaçamento entre as colunas. Você pode ampliar as colunas e modificar o uv.x - 0.5 + 0.2 * wobblebit para adicionar um deslocamento aleatório no eixo x. Você provavelmente também desejará tirar uma página do exemplo original mais uma vez e colocar duas camadas de fluxos de tamanhos diferentes umas sobre as outras para obter uma aparência menos uniforme.

Jimmy
fonte
@DMGregory Noted. removendo comentário metaball
Jimmy
A trilha em si pode ser feita através de um buffer, desbotando (retorne OldValue * .95 + newdiskposition). Geralmente as pessoas usam o ruído Perlin para perturbar uma linha reta.
Seyed Morteza Kamali
algo como este shadertoy.com/view/4dy3zR Tentei fazer trilhas barulhentas, mas não consegui #
Seyed Morteza Kamali
7

você pode fazer esse efeito seguindo as etapas abaixo:

Partícula

Partícula

RenderTextuer

você pode armazenar o resultado usando RenderTexture. este é um exemplo de multipass em shadertoy:

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

iñigo quilez: Shadertoy usa várias passagens, uma por "Buffer". Como o nome indica, isso passa armazena os resultados em um buffer. Um buffer é apenas uma textura. O Unity também permitirá que você renderize texturas.

Criei uma câmera para Rendering particulas to RenderTexture:

machado

RenderTexture

GrabPassing

você pode pegar o passe para aplicar a distorção

Eu expliquei isso neste post:

Como replicar o efeito de partícula de distorção do Quantum Break?

Borrão

usando alfa em cores ao longo da vida, temos um desfoque simples

alphaovertime

gradiente

para obter melhores resultados, é melhor usar o desfoque simples, mas como conseguimos o desfoque?

Matriz de convolução

No processamento de imagens, um núcleo, matriz de convolução ou máscara é uma matriz pequena. É usado para desfoque, nitidez, gravação, detecção de bordas e muito mais. Isso é realizado fazendo uma convolução entre um kernel e uma imagem.

para mais detalhes, siga este link

Núcleo

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

Borrão de caixa

Um desfoque de caixa (também conhecido como filtro linear de caixa) é um filtro linear de domínio espacial no qual cada pixel na imagem resultante tem um valor igual ao valor médio dos pixels vizinhos na imagem de entrada. É uma forma de filtro passa-baixo ("desfocagem"). Um desfoque de caixa 3 por 3 pode ser escrito como matriz

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

Repetição

você pode usar Rendertexture para armazenar o quadro anterior. repetindo isso, você obtém desfoque.

0fe28c6167db2132d4bb8677fc1b2050 - leandro-erlich-argentina

Normal

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

record_2019_03_03_21_35_45_417

Conclusão

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

sem usar alfa em cores durante a vida útil:

record_2019_03_03_21_48_36_273

usando alfa em cores ao longo da vida útil:

record_2019_03_03_21_48_19_786

A fonte está disponível:

https://github.com/smkplus/RainDrop

Tem mais!

também você pode fazer ondulações

record_2019_03_04_22_10_25_457

Links Úteis

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
fonte
1

Na verdade, havia uma pergunta sobre isso anos atrás, mas ela não se refere à Unidade (infelizmente). Se você olhar o slide 57 da apresentação vinculada, eles mencionarão uma abordagem baseada em grade.

Há uma pergunta um pouco relacionada à Física SE que você pode achar interessante. O link para droplet.pdf na questão vinculada está quebrado, mas ainda está no Wayback Machine. Ele aborda algumas das matemáticas da água que escorrem de alguns tipos de superfícies. As gotas preferem viajar em caminhos usados ​​anteriormente por gotas de chuva anteriores, por exemplo (consulte a p926).

Você provavelmente poderia modelar as cabeças e caudas de cada "gota de chuva" e permitir que ela ziguezagueie um pouco. Quando duas gotas de chuva alongadas colidem, suponho que você possa combiná-las em uma gota de chuva maior e mais rápida. O volume de água permanece o mesmo. Ele está apenas sendo movido e modelado pelas forças da gravidade, adesão ao vidro e coesão.

David A
fonte