// rain and snow of the gpu (opengl46_gpu_particles), in every colour pass which draws them: a thread per drop or flake. the // particles fill three boxes around the camera, a small dense one and two larger ones, each repeated along the world axes: // a particle keeps its place in the box, moved by how far the wind took them all and how far it fell, so nothing is kept // from frame to frame. what the camera sees is lit (particle_light.glsl) and drawn as a streak from where it is to where // it was a moment ago, seen from the moving camera (particles_precipitation.vert) #include #include #include #include layout(local_size_x = PARTICLE_GROUP_SIZE) in; // x, y: first particles of the second and of the third box, z: particles, w: 1 for snow, 0 for rain layout(location = 0) uniform uvec4 counts; // xyz: how far the wind took the particles less where the camera is, in each box (metres, 0 to the size of the box); w: how // far they fell in it, 0 to 16 sizes of the box layout(location = 1) uniform vec4 offsets[3]; // xyz: size of each box, metres; w unused layout(location = 4) uniform vec4 sizes; // xyz: velocity of the wind less the one of the camera, m/s; w: speed of the fall layout(location = 5) uniform vec4 velocity; // x: seconds (for the flutter of the flakes), y: how long a streak lasts, seconds, z: opacity, w: places for the draws layout(location = 6) uniform vec4 timing; // the world axes around the camera to the view space of the pass layout(location = 7) uniform mat4 view; // y: how much a particle scatters the light onwards, z: brightness layout(location = 11) uniform vec4 options; layout(location = 12) uniform vec4 planes[5]; void main() { uint index = gl_GlobalInvocationID.x; if (index >= counts.z) return; uint box = (index < counts.x) ? 0U : (index < counts.y) ? 1U : 2U; float size = sizes[box]; bool snow = counts.w != 0U; uint state = particle_hash(index * 3U + 0x9E3779B9U); vec3 home = vec3(particle_random(state), particle_random(state), particle_random(state)) * size; // the speed of the fall is (16 + k) / 16 of the common one, so a whole number of sizes of the box (16 of them) of the common // fall is a whole number of them for every particle, and the fall can be kept within 16 sizes float pace = (16.0 + floor(particle_random(state) * 7.0) - 3.0) / 16.0; float width = snow ? mix(0.004, 0.012, particle_random(state)) : mix(0.0012, 0.0025, particle_random(state)); vec3 move = vec3(velocity.xyz) + vec3(0.0, -velocity.w * pace, 0.0); vec3 place = home + offsets[box].xyz - vec3(0.0, offsets[box].w * pace, 0.0); if (snow) { // the flakes flutter about their way float amplitude = mix(0.1, 0.35, particle_random(state)); vec2 frequency = mix(vec2(1.5), vec2(5.0), vec2(particle_random(state), particle_random(state))); vec2 phase = vec2(particle_random(state), particle_random(state)) * 6.2831853; vec2 angle = frequency * timing.x + phase; place.xz += amplitude * sin(angle); move.xz += amplitude * frequency * cos(angle); } // within the box around the camera vec3 world = mod(place, size) - 0.5 * size; float opacity = timing.z; // fading at the walls of the box, where the particles wrap around, and right at the camera vec3 walls = abs(world) / (0.5 * size); opacity *= 1.0 - smoothstep(0.7, 1.0, max(max(walls.x, walls.y), walls.z)); opacity *= smoothstep(0.15, 0.6, length(world)); if (opacity <= 0.002) return; vec3 tail = -move * timing.y; float streak = length(tail); if (streak > 3.0) tail *= 3.0 / streak; if (false == particle_visible(planes, world + 0.5 * tail, 0.5 * length(tail) + width)) return; vec3 color = particle_light((view * vec4(world, 1.0)).xyz, world, options.y); particle_emit(world, width, tail, color * options.z, opacity, uint(timing.w)); }