GLSL Basic Snow Shader

shaders

I made this shader because I've wanted to create some animations for the holiday season for a long while, but I just couldn't grok shaders..

AI is a phenomenal tool for learning though, and it helped me a lot here.

The problem with shaders is that they're extremely hard to debug.

You have to basically track every single function's input and output range, and you also have to know what kind of curve they describe.

This becomes pretty difficult when you're beginning to combine multiple functions together.

Anyways, here's the shader with some settings:

hide controls

Controls

Code

The vertex shader is just a big triangle that contains the entire screen from -1 to +1, so we can draw to the entire canvas (screen) in the fragment shader.

I've been lazy and I just redefined some variables to make the fragment shader work in WebGL. The original shader was made in Shadertoy, which uses slightly different variable names.

Here's the fragment shader:

webgl
1precision highp float;
2
3uniform float u_time;
4uniform vec2 u_resolution;
5
6uniform float u_speed_min;
7uniform float u_speed_max;
8uniform float u_radius_min;
9uniform float u_radius_max;
10
11
12float hash(float n)
13{
14    return fract(sin(n) * 43758.5453123);
15}
16
17vec2 hash2(float n)
18{
19    return vec2(hash(n), hash(n + 17.0));
20}
21
22
23void main()
24{
25    // translate shadertoy vars to built in opengl es ones
26    vec2 iResolution = u_resolution;
27    float iTime = u_time;
28    vec4 fragColor;
29    vec2 fragCoord = gl_FragCoord.xy;
30    // -------------------------------------
31
32    vec2 uv = fragCoord / iResolution.xy;
33    uv.x *= iResolution.x / iResolution.y; // aspect correction
34
35    float t = iTime;
36
37    float col = 0.0;
38
39    const int COUNT = 70;
40
41    // loop over every possible snowflake 
42    // render it if pixel is inside one 
43    for (int i = 0; i < COUNT; i++)
44    {
45        float fi = float(i);
46
47        // random base position
48        vec2 r = hash2(fi);
49
50        // x position, corrected for screen aspect
51        float x = r.x * (iResolution.x / iResolution.y);
52        
53        // y position, falling over time
54        float speed = mix(u_speed_min, u_speed_max, r.y);
55        float y = 1.0 - fract(t * speed + r.y);
56
57        vec2 pos = vec2(x, y);
58
59        // random circle size
60        float radius = mix(u_radius_min, u_radius_max, hash(fi + 42.0));
61
62        // circle sdf, blurred edges
63        float d = length(uv - pos);
64        float blur = mix(0.05, 0.1, radius);
65        float circle = smoothstep(radius, radius - blur, d);
66
67        col += circle;
68    }
69
70    col = clamp(col, 0.0, 1.0);
71
72    fragColor = vec4(vec3(col), 1.0);
73
74    // convert back to opengl es compatible vars
75    gl_FragColor = fragColor;
76}