Yes I’m using atomic counters in this section of my project. pCount gets reset to zero every frame. At the end of the frame, it’s ideally about the same as the number of pixels in the image. So some particles get deleted (not copied over), some get copied over as is, some get copied twice. Hopefully you end up with a number of particles equal to what the size of the buffer can handle. If pCount goes above that, which is possible, don’t write at all, just return in main.

What I implemented was “Weighted Linde-Buzo-gray Stippling” https://kops.uni-konstanz.de/bitstream/handle/123456789/41075/Deussen_2-gu29mv4u87jh2.pdf;jsessionid=CBA553E15000B0419FF1BD6A5FB9B1A9?sequence=1

One step is getting a voronoi diagram rendered at a big resolution like 1280x720 or 1920x1080. You can do this with either the cone method or jump-flood-algorithm. Every pixel needs to be an integer indicating which voronoi seed is closest. No anti-aliasing is allowed.

Next step is a shader with ImageAtomicAdd. The number of invocations needs to be the size of the input image, so 1280x720=921600. The pixel dimensions needs to be the number of voronoi seeds*3. This is because ImageAtomicAdd only works on integers, not vec4, and we need 3 numbers calculated for each seed. Those numbers are the centroid x, the centroid y, and the overall density. So for every pixel in the input image, visit it in the compute shader, determine what index voronoi cell it is and do some stuff to determine what to store in the output pixels.

In your example, if you put an analyze TOP set to average and then multiply the red channel by 16 (number of pixels) you get about the same sum as what the compute shader reports.