> Well, for 40 bouncing circles, on a 700x500 grid, that would be on the order of 14 million operations. If we want to have a nice smooth 60fps animation, that would be 840 million operations per second. JavaScript engines may be fast nowadays, but not that fast.
The math is super-cool, and efficiency is important for finding isosurfaces in higher dimensions, but those aren't really scary numbers for normal programs. Just tinting the screen at 2880x1800 is ~2 million operations per frame. GPUs can handle it.
A simple way to render is to draw a quad for the metaball, using the metaball kernel function in the fragment shader. Use additive blending while rendering to a texture for the first pass, then render the texture to screen with thresholding for the second pass. The end result is per-pixel sampling of the isosurface.
Admittedly, it's kind of a brute-force solution, but even the integrated GPU on my laptop can render thousands of metaballs like that at HiDPI resolutions.
(Specifically, I use a Gaussian kernel for my metaballs. It requires exp, which is more expensive computationally than a few multiplies. I render 1500 of them at 2880x1671 at 5ms per frame on an Intel Iris Pro [Haswell].)
Though, the work scales with fragment count, so a few large metaballs may be as costly many smaller ones. For large numbers of metaballs, you probably also want to use instancing so you'd need OpenGL ES 3.0 / WebGL 2.0 which are fairly recent.
But 40 metaballs with a simple kernel at 700x500? That's easy for a GPU.
I remember what I did to optimize my own metaballs: after rendering all different sizes that were used on their own screen at (0, 0), I would only add the values at relative positions (signs removed) of their respective preset map.
It does take some memory, but n operations per pixel for each frame for n balls plus the overhead of transforming into actual color was still pretty great. Instead of saying "GPU can do this", I'd rather ask, hey, can we do even better than that?
Feels more organic to me if the original metaball gets smaller as the other one moves out (like its stealing material). Haven't worked out the correct math but a quick PoC is here:
Keeping 100% accurate constant area would require a pretty insane closed form equation. Even ignoring the amount of area added by the stretched portions.
Oh wow, it's a gaussian blur and basically a threshold function (boosting the contrast) using SVG filters! I didn't know this was possible. Very clever!
Interesting approach! Coincidentally, I published an article [0] on this very topic last month. It uses sampling, so it's close to the approach mentioned in the Jamie Wong article you (and I) linked to, but with a path-tracing step capable of producing an SVG path definition. I'd be interested to see how the performance of these two methods stack up to each other for a given quality level.
Much better. There's definitely many improvements that could be done, but that was the main one. The other big one is how the bigger disc doesn't change size.
The approximation OP does is a good start but still far from being real metaballs.
I'm extremely impressed with the implementation. I'm not sure what I would say if presented with a design like this slider for web. Wouldn't have imagined it would work this beautifully as well.
It's possible to do this somewhat efficiently beyond two balls with GLSL and lots of uniforms (or a UBO), since metaballs from the graphics perspective are really just distance fields.
If you want more than a few balls, you can do it in two passes: one to produce the distance field, and one to threshold it.
As an added benefit, it's straightforward to generalize these approaches to any two-dimensional continuous function.
I did something fairly similar to this here: https://codepen.io/thomcc/pen/vLzyPY (I need to look into why this isn't running at 60fps anymore on my laptop, it certainly used to...)
The big difference is that it prerenders a gradient for each ball (it uses html5 canvas for that, but doing it with webgl is completely doable, although a bit more work), which is used as a distance field.
The math can be optimized by at least an order of magnitude.
Trigonometry functions are expensive, especially the reverse ones.
If v=0.5, see [1] for how to find out sine/cosine of a maxSpread * v. For angleBetweenCenters + maxSpread * v, see [2] for how to find sine + cosine of a sum of angles.
If you’ll do all that math in the symbolic form (you can use Maple or Mathematica or something similar), you’ll get the equivalent formulae for p1-p4 that won’t use any trigonometry, only simple math and probably a square root.
Implemented a 2D angle class for cases like that, where you would otherwise use these slow trigonometry functions. It’s C++ but should be easy to convert to JavaScript or any other OO language.
After years of living in the US, I still have trouble that the word is "cockpit" not "cocktip". That became hilariously obvious when at work we had to use a library that uses the term "cockpit" for one of its main components.
I just started learning GLSL shaders. As practice, I wrote a psuedo-metaball joystick. I didn't know about metaballs, but now that I do I can do some more research and improve my next iteration.
About 2 minutes in there's an excellent realtime metaballs implementation that ran smoothly on a 486-66mhz. Metaballs were an extremely popular effect in the early 90's.
Paper.js is truly a great source of vector drawing tricks.
Curious how difficult it would be to extend this technique beyond two circles. Might have to dust off some old experiments ... :)
Metaballs are always nice, but I think this page (that was linked in the article) that shows compass&straight-edge constructions to be especially nifty:
During or just before WW2, Roy Liming developed analytic techniques for calculating a similar class of blend or fillet. They were taken up in aircraft design, a field that I can't imagine ever using implicit surfaces! I think it was Edgar Schmued's design for the P-51 Mustang that famously used Liming's work.
We've use the blur+contrast approach successfully in EventDrops [1], a time series visualisation based on d3.js. It all happens client-side, with OK performance. Not sure the SVG approach brings more in this case.
[+] [-] slavik81|8 years ago|reply
> Well, for 40 bouncing circles, on a 700x500 grid, that would be on the order of 14 million operations. If we want to have a nice smooth 60fps animation, that would be 840 million operations per second. JavaScript engines may be fast nowadays, but not that fast.
The math is super-cool, and efficiency is important for finding isosurfaces in higher dimensions, but those aren't really scary numbers for normal programs. Just tinting the screen at 2880x1800 is ~2 million operations per frame. GPUs can handle it.
A simple way to render is to draw a quad for the metaball, using the metaball kernel function in the fragment shader. Use additive blending while rendering to a texture for the first pass, then render the texture to screen with thresholding for the second pass. The end result is per-pixel sampling of the isosurface.
Admittedly, it's kind of a brute-force solution, but even the integrated GPU on my laptop can render thousands of metaballs like that at HiDPI resolutions.
(Specifically, I use a Gaussian kernel for my metaballs. It requires exp, which is more expensive computationally than a few multiplies. I render 1500 of them at 2880x1671 at 5ms per frame on an Intel Iris Pro [Haswell].)
Though, the work scales with fragment count, so a few large metaballs may be as costly many smaller ones. For large numbers of metaballs, you probably also want to use instancing so you'd need OpenGL ES 3.0 / WebGL 2.0 which are fairly recent.
But 40 metaballs with a simple kernel at 700x500? That's easy for a GPU.
[+] [-] skeoh|8 years ago|reply
[+] [-] mar77i|8 years ago|reply
It does take some memory, but n operations per pixel for each frame for n balls plus the overhead of transforming into actual color was still pretty great. Instead of saying "GPU can do this", I'd rather ask, hey, can we do even better than that?
[+] [-] femto113|8 years ago|reply
https://codepen.io/femto113/pen/MEZava
[+] [-] fergyfresh|8 years ago|reply
[+] [-] XaspR8d|8 years ago|reply
[+] [-] winkerVSbecks|8 years ago|reply
[+] [-] 11235813213455|8 years ago|reply
[deleted]
[+] [-] mikewhy|8 years ago|reply
Article for more detail: https://tympanus.net/codrops/2015/03/10/creative-gooey-effec...
[+] [-] andai|8 years ago|reply
[+] [-] NKCSS|8 years ago|reply
[+] [-] dmschaab|8 years ago|reply
[0] https://eightsquaredsoftware.com/articles/metaball.html
[+] [-] robocat|8 years ago|reply
Beautiful article.
[+] [-] winkerVSbecks|8 years ago|reply
[+] [-] pault|8 years ago|reply
[+] [-] rosstex|8 years ago|reply
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] panic|8 years ago|reply
[+] [-] zaroth|8 years ago|reply
[+] [-] ehsankia|8 years ago|reply
The approximation OP does is a good start but still far from being real metaballs.
[+] [-] bpicolo|8 years ago|reply
[+] [-] thoughtpalette|8 years ago|reply
Amazing showcase.
[+] [-] microcolonel|8 years ago|reply
It's possible to do this somewhat efficiently beyond two balls with GLSL and lots of uniforms (or a UBO), since metaballs from the graphics perspective are really just distance fields.
If you want more than a few balls, you can do it in two passes: one to produce the distance field, and one to threshold it.
As an added benefit, it's straightforward to generalize these approaches to any two-dimensional continuous function.
[+] [-] woodrowbarlow|8 years ago|reply
https://codepen.io/keithclark/pen/sEbFz
[+] [-] yoklov|8 years ago|reply
The big difference is that it prerenders a gradient for each ball (it uses html5 canvas for that, but doing it with webgl is completely doable, although a bit more work), which is used as a distance field.
[+] [-] garaetjjte|8 years ago|reply
[+] [-] winkerVSbecks|8 years ago|reply
[+] [-] winkerVSbecks|8 years ago|reply
[+] [-] Const-me|8 years ago|reply
Trigonometry functions are expensive, especially the reverse ones.
If v=0.5, see [1] for how to find out sine/cosine of a maxSpread * v. For angleBetweenCenters + maxSpread * v, see [2] for how to find sine + cosine of a sum of angles.
If you’ll do all that math in the symbolic form (you can use Maple or Mathematica or something similar), you’ll get the equivalent formulae for p1-p4 that won’t use any trigonometry, only simple math and probably a square root.
[1] https://en.wikipedia.org/wiki/List_of_trigonometric_identiti... [2] https://en.wikipedia.org/wiki/List_of_trigonometric_identiti...
[+] [-] Const-me|8 years ago|reply
https://gist.github.com/Const-me/46f90ce7fc2bc65dc12a41442ed...
[+] [-] santaclaus|8 years ago|reply
I once reviewed an academic paper at a major CS conference that misspelled metaballs as meatballs throughout.
[+] [-] dguaraglia|8 years ago|reply
[+] [-] jlg23|8 years ago|reply
[1] https://www.youtube.com/watch?v=OonDPGwAyfQ
[+] [-] philipov|8 years ago|reply
[+] [-] mileycyrusXOXO|8 years ago|reply
Touch blob joystick shader: https://www.shadertoy.com/view/4lfcRf
[+] [-] spitfire|8 years ago|reply
About 2 minutes in there's an excellent realtime metaballs implementation that ran smoothly on a 486-66mhz. Metaballs were an extremely popular effect in the early 90's.
[+] [-] twic|8 years ago|reply
http://www.pouet.net/prod.php?which=1023
http://www.pouet.net/prod.php?which=911
How about first on the C64? Here's Booze in 2010:
http://www.pouet.net/prod.php?which=56003
[+] [-] shove|8 years ago|reply
[+] [-] philipov|8 years ago|reply
[+] [-] zokier|8 years ago|reply
http://www.mathopenref.com/consttangentsext.html
[+] [-] emmelaich|8 years ago|reply
[+] [-] theoh|8 years ago|reply
Liming wrote a book, but it's rare. Some technical discussion towards the end of this page: http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/BOWY...
[+] [-] ladon86|8 years ago|reply
[+] [-] easytiger|8 years ago|reply
* https://www.gamasutra.com/view/feature/2438/how_to_prototype...
[+] [-] asadlionpk|8 years ago|reply
I had to struggle with metaball rendering on canvas back then. It was so slow. Now I guess a pixel shader in webGL can do a better job.
Check this out too: https://asadmemon.com/SPHjs/ source: https://github.com/asadm/SPHjs
[+] [-] christotty|8 years ago|reply
http://jamie-wong.com/2014/08/19/metaballs-and-marching-squa...
[+] [-] fzaninotto|8 years ago|reply
[1] https://marmelab.com/EventDrops/