Voronoi Diagrams on the GPU (2016)
rykap.com
rykap.com
I rewrote my diffusion curve[1] rendering code to GPU the other year and rendering diffusion curves is basically analogous to voronoi rendering (your first pass should be seeding a canvas with the closest source color, then an varying amount of blurring depending on how the distance to the closest source, the max being canvas resolution so it can be done in log(res) passes).
Since the curves (ie "voronoi seeds") are splines you want to render them at canvas resolution, so for just a simple circle on a 2048x2048 canvas you're looking at about 4-8k of seeds just for this simple case, but reducing the size of the cone isn't an option (the center of the canvas will have a 2k pixel distance to the closest pixel), so you'd basically be rendering a "large" amount of huge cones.
Anyhow I found a solution to the nearest pixel+distance problem by running a couple of repeated axis aligned "run" passes(kind of like flood-filling), had I know of this JFA algo I probably would've used that. I did stumble upon the cone rendering but the overdraw issue concerned me.
If I get some time over I should revisit that code and see if JFA would be faster.. the number of passes should be in the same ballpark but JFA should be more paralell since my compute shader had loops and was parallelism bound by each axis dimension.
> On the same machine, the instancing version can do a few thousand seed vertices (an order of magnitude more) at 60 frames-per-second, after which it becomes bandwidth saturated.
If you have a powerful GPU you can get even faster results. I have a project that does Lloyd's relaxation using voronoi and for me the using cones with a depth buffer gave the best result.
Real diffusion curve images would multiply that by an order or two at the least depending on complexity of the image (the algo I wrote as well as JFA might have a higher fixed number of iterations but it's fixed so the number of seeds don't matter).
Besides, GPU's have turned more and more general so even if there's optimizations for the triangle rendering the compute case will probably gain even more from more recent GPUs.
Optimizing for GPU's can be funny, and I wouldn't be surprised if the break-point on where the tri-fans are faster is higher than I expect, but at the same time unless it's a tile-based renderer I'm pretty sure there will be a break-point (also tilebased renderers can sometimes fall of performance cliffs in special cases).
The author doesn't mention it, so I'm not sure if he noticed - but this method is trivially extensible to weighted Voronoi graphs.
All you need to do is change the gradients of the cone such that d(radius)/d(z) = weight (and that makes things significantly easier when you need to deal with curved inter-cell boundaries).
This potentially could be used to generate 3D voxels, but you'd need depth buffer support in 4 dimensions.
No shaders needed at all.
https://www.researchgate.net/figure/PSM-2D-bitmap-truncated-...
Here is an example for a CPU-based implementation: https://github.com/983/df
It is not difficult to implement for the GPU because the algorithm can be run in parallel for each image row and column, assuming general GPU compute capability, which admittedly is still lacking in some browsers.
One thing that is interesting to point out, is how these approaches differ from the classic approach: Fortune's algorithm: https://en.wikipedia.org/wiki/Fortune%27s_algorithm
One particular differential to make is the "where" part here. O(N) always sounds good, but in this particular discussion we note that O(N) is the number of pixels, whereas in Fortune's algorithm O(N log N) time and O(N) space is the number of points. I wonder which one is faster and if it depends on the input.
For example, I imagine a massive field with quintillions of pixels and only two or three points - it seems Fortune's would be the way to go.
Now I imagine another massive field with quintillions of pixels and trillions of points. Now I might be interested in doing a distance field like approach.