# Algorithm for a rain effect (Shaders)

I'm trying to implement a rain effect using shader, I use godot 3-alpha, which uses a simplified GLSL 3.0 language. But I'm just finding very complex examples for me, I understand little about shaders, I wanted to achieve something very basic even to be able to walk. I thought of doing the following, moving the texture with just a few painted pixels. Yet I still do not know where to start.

I found this code, I really wanted to understand what is done in it:

http://www.glslsandbox.com/e#36547.0

I managed to translate the code for godot 3, but it does not work very well, I believe this line is to blame:

p=0.5+0.35*sin(11.0*fract(sin((s+p+scale)*mat2(vec2(7,3),vec2(6,5)))*5.0))-f;


What really is done that line? I can not understand what he does.

I did so:

shader_type canvas_item;

uniform float direction;
uniform float velocity = 1.0;
uniform float intensity;

float snow(vec2 uv, float scale, float time)
{
float w = smoothstep(1.0,0.0,-uv.y*(scale/10.0));
if(w<0.1){return 0.0;}
uv+=time/scale;
uv.y-=time*velocity/scale; //VELOCITY
uv.x+=sin(uv.y+time*0.5)/scale;
uv*=scale;
vec2 s=floor(uv);
vec2 f=fract(uv);
vec2 p;
float k=3.0;
float d;
vec2 t = (s*p*vec2(scale))*mat2(vec2(7.0, 3.0), vec2(6.0,5.0));
p.y=-(0.5+0.35*sin(11.0*fract(sin((s.y+p.y+scale)*t.y)*5.0))-f.y);
p.x = f.x;

d=length(p);
k=min(d,k);
k=smoothstep(0.0,k,sin(f.x+f.y)*0.01);
return k*w;
}

void fragment(){
vec2 uv=(COLOR.xy*2.0-SCREEN_UV.xy)/min(SCREEN_UV.x,SCREEN_UV.y);
float c=smoothstep(1.0,0.3,clamp(uv.y*0.3+0.8,0.0,0.75));
c+=snow(uv, 30.0, TIME)*0.3;
c+=snow(uv, 20.0, TIME)*0.5;
c+=snow(uv, 15.0, TIME)*0.8;
c+=snow(uv, 10.0, TIME);
c+=snow(uv, 8.0, TIME);
c+=snow(uv, 6.0, TIME);
c+=snow(uv, 5.0, TIME);
c+=snow(uv, 2.0, TIME);
COLOR = vec4(vec3(c),0.5);

}


and this happens:

I have discovered that by decreasing the value I am passing to the scale, it increases the "particles", I mean how octaves in a noise, the smaller the number last, the greater the peaos generated. But my screen seems to be divided in two, cut by a diagonal line, I do not understand much because. I realize that here is the problem:

vec2 uv=(COLOR.xy*2.0-SCREEN_UV.xy)/min(SCREEN_UV.x,SCREEN_UV.y);


Up 1:

I managed to improve with this:

vec2 uv= UV;


now it's like this:

but what he wanted was to be able to pass three uniforms, direction, velocity and intensity, and to be able to control the direction (right equer), the speed with which they fall, the intensity (higher or lower particles, more or less drops). But I still do not understand how well it is done to do that. Even more that what I will do is rain, I have to understand what is done with the colors to get the idea of rain and not snow.

Up 2:

I was stirring here and the velocity, intensity and direction I was able to configure passing a uniform. But I can not change the color of the drops, it always comes out black and white. How would you put a blue color for example?

I try to change the color to red for example using the mix() function:

vec3 col2 = vec3(1.0, 0.0, 0.0);
vec3 resultColor= mix(vec3(c), col2, 0.7);
COLOR = vec4(vec3(resultColor),0.3);


and I get this:

I wanted to change only the color of the drops?

R= I got it like this:

 COLOR = vec4(vec3(0.3, 0.3, c),0.5);


but I'm not very fond of the result, it's very bright, and my breasts are too big, I'll fix it.

• It's difficult to know from this question what specific kind of help you need, or what answers might count as "correct" — and you try editing your question to elaborate about the specific effect you're trying to achieve, or a specific issue you've encountered in your work so far? Commented Oct 1, 2017 at 23:59
• @gregory I edited. Commented Oct 2, 2017 at 4:07
• Don't use shadertoy shaders for a tutorial, most of them are pretty badly written. Commented Oct 2, 2017 at 7:09
• @PerduGames the idea behind should be quite simple. The snowfall or rainfall effect simulates particles falling assuming each particles describes a sine wave path. The sine alters the horizontal displacement while the vertical displacement is usually constant (snowflakes fall at a fixed rate). Now, if you generate particles that have different sizes (large to small), put the larger ones to move on a larger sine (bigger amplitude) and the smaller ones on a smaller sine, and you'll get a pseudoparallax effect of layered snow falling, giving a sense of depth. That's it. Try to do it from scratch. Commented Oct 2, 2017 at 9:08
• Can you describe in more detail what "does not work very well" about your translation? It's not easy to guess from an isolated line of code to what it will do on the screen, so since you have the only running copy of your version, you can help us out by showing us the result and describing in exactly what way it differs from what you want it to do. Commented Oct 2, 2017 at 9:58

Changing the colour of the snowflakes isn't very hard, here's what you need to do:

void fragment(){
vec2 uv=(COLOR.xy*2.0-SCREEN_UV.xy)/min(SCREEN_UV.x,SCREEN_UV.y);
float c=smoothstep(1.0,0.3,clamp(uv.y*0.3+0.8,0.0,0.75));

//Put the snow into a separate vec3 so we can colour it separately
vec3 s = vec3(0.);
s+=snow(uv, 30.0, TIME)*0.3;
s+=snow(uv, 20.0, TIME)*0.5;
s+=snow(uv, 15.0, TIME)*0.8;
s+=snow(uv, 10.0, TIME);
s+=snow(uv, 8.0, TIME);
s+=snow(uv, 6.0, TIME);
s+=snow(uv, 5.0, TIME);
s+=snow(uv, 2.0, TIME);

//Change this value here to whatever colour you want the snow to be
s *= vec3(1.0,0.1,0.4);

//Add the snow back with the background (stored in c) after it's coloured
COLOR = vec4(vec3(c) + s,0.5);
}


In this code we separate the snow into a separate variable (which uses a negligible amount of memory) so that we can tint it any colour we like after all the snow is added with it affecting the rest of the scene by multiplying (which is really fast on a GPU) it by a colour.

• I had a problem with my operating system + burned network card + other things and I'm reconfiguring everything here in my work environment, when I finish soon I'll see, thanks in advance. Commented Oct 13, 2017 at 20:06
• it worked, only remembering that it should be like this uv=vec2((2.0+UV.x), (2.0-UV.y));, if it would not stay with that bug that said in the question, the first image. Commented Oct 16, 2017 at 18:19

I see multiple problems with your code.

1. The line vec2 uv=(COLOR.xy*2.0-SCREEN_UV.xy)/min(SCREEN_UV.x,SCREEN_UV.y) doesn't match the original code you've linked at all. Try vec2 uv=(FRAGCOORD.xy*2.0-resolution.xy)/min(resolution.x,resolution.y) instead, and pass resolution as a uniform vec2.

2. Both the original code and your Godot port of it have an uninitialized variable p. You could initialize p to vec2(0.0), but the better solution in this case would be to just remove it from the right-hand side of the equations that set p. Even the original shader code doesn't work on my Mac in Safari because of this bug, although it works in Chrome on the same computer (that's the nature of uninitialized variables). With p properly initialized before use or removed from the right hand side of the equations that set it, the code works fine in Safari too.

3. Your decomposed equations for p don't seem to match the original code. A direct port would be: p=.5+.35*sin(11.0*fract(sin((s+p+scale)*mat2(vec2(7.0,3.0),vec2(6.0,5.0)))*5.0))-f

4. You define direction and intensity, but I don't see any references to them later in your code.

5. You have the sign of the equation for uv.y flipped.

6. You've removed the original author's name. Although you do provide a link to the original work, considering people may copy the code directly from here, one should always make sure the code gives proper credit to the original author.

After all these corrections, I get proper snow effect in Godot 3.0 that matches the original at glslsandbox.com. Here's a final working code:

shader_type canvas_item;

//--- hatsuyuki ---
// by Catzpaw 2016
//
// Modified for Godot; original code can be found at http://www.glslsandbox.com/e#36547.0

//#extension GL_OES_standard_derivatives : enable

uniform vec2 resolution;

float snow(vec2 uv, float scale, float time)
{
float w=smoothstep(1.0,0.0,-uv.y*(scale/10.0));if(w<.1)return 0.0;
uv+=time/scale;uv.y+=time*2.0/scale;uv.x+=sin(uv.y+time*.5)/scale;
uv*=scale;vec2 s=floor(uv),f=fract(uv),p;float k=3.0,d;
p=.5+.35*sin(11.0*fract(sin((s+scale)*mat2(vec2(7,3),vec2(6,5)))*5.0))-f;d=length(p);k=min(d,k);
k=smoothstep(0.0,k,sin(f.x+f.y)*0.01);
return k*w;
}

void fragment( )
{
vec2 uv=(FRAGCOORD.xy*2.0-resolution.xy)/min(resolution.x,resolution.y);
vec3 finalColor=vec3(0);
float c=smoothstep(1.0,0.3,clamp(uv.y*.3+.8,0.0,.75));
c+=snow(uv,30.0,TIME)*.3;
c+=snow(uv,20.0,TIME)*.5;
c+=snow(uv,15.0,TIME)*.8;
c+=snow(uv,10.0,TIME);
c+=snow(uv,8.0,TIME);
c+=snow(uv,6.0,TIME);
c+=snow(uv,5.0,TIME);
finalColor=(vec3(c));
COLOR = vec4(finalColor,1.0);
}


The code above will create a parameter named resolution under the Shader Material settings in Godot. Be sure to set it to a reasonable value; otherwise the code won't work.

P.S. I've left the original author's formatting as is. As unreadable as their coding style is, this should allow the code to be compared to the original easily.