How to implement Metaball Effect with GLSL

HyunsuYu
3 min readJun 3, 2024

--

The Metaball effect is one of the graphic effects, which is to realize an effect like a droplet or shadow gently pulling when an object and an object get closer to each other.​

Wikipedia describes the Metaball effect as follows

In computer graphics, metaballs, also known as blobby objects,​ are organic-looking n-dimensional isosurfaces, characterised by their ability to meld together when in close proximity to create single, contiguous objects.

In solid modelling, polygon meshes are commonly used. In certain instances, however, metaballs are superior. A metaball’s “blobby” appearance makes them versatile tools, often used to model organic objects and also to create base meshes for sculpting.

The technique for rendering metaballs was invented by Jim Blinn in the early 1980s to model atom interactions for Carl Sagan’s 1980 TV series Cosmos. It is also referred to colloquially as the “jelly effect” in the motion and UX design community, commonly appearing in UI elements such as navigations and buttons. Metaball behavior corresponds to mitosis in cell biology, where chromosomes generate identical copies of themselves through cell division.

Wikipedia

In other words, as shown in the image below, the Metaball effect is to be attracted to each other as you get closer to the distance between objects, and then become completely like an object at the end

The basic principle of Metaball is very simple

1. For all points (x, y) on the screen, find the distance from the desired point of the Metaball operation and add it to the buffer variable

2. If the value of the buffer variable is above the threshold, the corresponding positions (x, y) are in Metaball, and if it is below the threshold, they are outside Metaball

If the above principle is written in a formula, it is as follows

The above equation refers to in three dimensions. If you are going to implement the Metaball effect based on a point on a two-dimensional screen, you just need to adjust the function below slightly. At this time, the actual function shape based on the two-dimensional standard will look as follows

Code that actually implements this is as follows. In this case, the testing environment is Android’s Shader Editor

#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

uniform float time;
uniform int pointerCount;
uniform vec3 pointers[10];
uniform vec2 resolution;
uniform vec3 gravity;

int fillPointCount = 3;
float fillDistance = 250.0;

float GetMetaball(vec2 a, vec2 b);

void main(void)
{
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec3 color = vec3(uv, 0.25 + 0.5 * sin(time));

float sum = 0.0;
for (int n = 0; n < pointerCount; ++n)
{
float temp = 3.0 * GetMetaball(gl_FragCoord.xy, pointers[n].xy);

if(temp >= 0.000001)
{
sum += temp;
}
}

if(sum >= 0.00009)
{
color.xyz *= 1.2;
}

gl_FragColor = vec4(color, 1.0);
}

float GetMetaball(vec2 a, vec2 b)
{
return 1.0 / ((pow(b.x - a.x, 2.0) + pow(b.y - a.y, 2.0)));
}

--

--

HyunsuYu

As a game developer with Unity and C# as the main players, I've been working on a number of game development projects and side projects