
#define SRC(...) #__VA_ARGS__
SRC(
struct UBO
{
   float4x4 modelViewProj;
   float2 OutputSize;
   float time;
};
uniform UBO global;

struct PSInput
{
   float4 position : SV_POSITION;
   float2 texcoord : TEXCOORD0;
};

PSInput VSMain(float4 position : POSITION, float2 texcoord : TEXCOORD0)
{
   PSInput result;
   result.position = mul(global.modelViewProj, position);
   result.texcoord = texcoord;
   return result;
}

static const float baseScale = 3.5;  //  [1.0  .. 10.0]
static const float density   = 0.7;  //  [0.01 ..  1.0]
static const float speed     = 0.25; //  [0.1  ..  1.0]

float rand(float2 co)
{
   return frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453);
}

float dist_func(float2 distv)
{
   float dist = sqrt((distv.x * distv.x) + (distv.y * distv.y)) * (40.0 / baseScale);
   dist = clamp(dist, 0.0, 1.0);
   return cos(dist * (3.14159265358 * 0.5)) * 0.5;
}

float random_dots(float2 co)
{
   float part = 1.0 / 20.0;
   float2 cd = floor(co / part);
   float p = rand(cd);

   if (p > 0.005 * (density * 40.0))
      return 0.0;

   float2 dpos = (float2(frac(p * 2.0) , p) + float2(2.0, 2.0)) * 0.25;

   float2 cellpos = frac(co / part);
   float2 distv = (cellpos - dpos);

   return dist_func(distv);
}

float snow(float2 pos, float time, float scale)
{
   // add wobble
   pos.x += cos(pos.y * 1.2 + time * 3.14159 * 2.0 + 1.0 / scale) / (8.0 / scale) * 4.0;
   // add gravity
   pos += time * scale * float2(-0.5, 1.0) * 4.0;
   return random_dots(pos / scale) * (scale * 0.5 + 0.5);
}

float4 PSMain(PSInput input) : SV_TARGET
{
   float tim = global.time * 0.4 * speed;
   float2 pos = input.position.xy / global.OutputSize.xx;
   pos.y = 1.0 - pos.y; // Flip Y
   float a = 0.0;
   // Each of these is a layer of snow
   // Remove some for better performance
   // Changing the scale (3rd value) will mess with the looping
   a += snow(pos, tim, 1.0);
   a += snow(pos, tim, 0.7);
   a += snow(pos, tim, 0.6);
   a += snow(pos, tim, 0.5);
   a += snow(pos, tim, 0.4);
   a += snow(pos, tim, 0.3);
   a += snow(pos, tim, 0.25);
   a += snow(pos, tim, 0.125);
   a = a * min(pos.y * 4.0, 1.0);
   return float4(1.0, 1.0, 1.0, a);
};
)
