---
title: "Metaballs"
author: Mossfet
date: 2022-05-17T17:57:00+01:00
lastmod: 2022-05-24T21:58:00+01:00
draft: false
---

### 1: TIC-80

A few months ago, I stumbled across the concept of 'metaballs' on YouTube. I was already trying to work on a semi-successful 3D graphics project on the [TIC-80](https://tic80.com/), a fantasy console similar to the Pico-8, so when I gave up on that, I decided to try my hand at writing them there as well.

You can find the result [here](https://tic80.com/play?cart=2784)

It didn't go horribly, but I was pretty dissatisfied with the performance and the limitation to a few colour bands. The TIC-80's standard library is tiny, and doesn't seem to have any support for hardware acceleration or multithreading. The most efficient way I could find was essentially to run the calculation on every pixel in sequence on every frame. Combined with the inherent limitations of an interpreted language being run in a browser and it ran at around 10fps for three balls on my admittedly low-power Chromebook, and that's taking into account the low resolution of the TIC-80 - 240 by 136. If this were scaled up to something like 1080p it would probably run around 60 times slower. So I tried again

### 2: Godot

About a week ago, I decided to rewrite in in Godot. I ran a shader on it that did the calculations per-pixel, and instead of set colour bands I was able to make a gradient. I'm not entirely sure whether I prefer the bands or the gradient, but I'm glad I was able to try both. More importantly, it was able to run at 1080p at 60 frames per second, so the performance was more than satisfactory for me. I haven't published the code because frankly it's not that interesting, thought if you want it for whatever reason, message me. 

I'm currently working on a game in Godot, so I'm glad to take all the excuses to learn new concepts in it as I can get. 

### 3: OpenGL

I had done [an OpenGL course](https://learnopengl.com/) around a year ago, and wanted to have a go at Metaballs on there as well. As the code for this project is largely based on the OpenGL course, it is written in C++ and uses the GLFW library. I followed the course up to drawing a triangle, but instead focused on making two triangles that form a rectangle that covers the whole screen (I am told that this is how game developers usually apply a shader to the whole screen). The shader was very similar to the one done in Godot but had some very cursed code that attempted to convert between the coordinate system that the ball uses (-1.0, -1.0 for the top-left, 1.0, 1.0 for the top right), and normal pixel coordinates. 
I could refactor it into something better but frankly I am tired and have a short attention span. 
```C
// This is not happy code
return (500.0 * ball.z / (pow(abs(gl_FragCoord.x - (0.5 + ball.x / 2) * 1920.0), 2) + pow(abs(gl_FragCoord.y - (0.5 + ball.y / 2) * 1080.0), 2)));
```

All in all the code took me around four straight hours to write. Sadly, as the laptop it was written on has now died due to a faulty charging port (might see about repairing it in future), the program is lost. I didn't get a chance to benchmark it, either. If I do manage to fix it, I will back up the program and benchmark it against the Vulkan implementation. 

### 4: Vulkan

This was probably a mistake. Being a much more complex to use graphics API than OpenGL, it was far more verbose to complete. Like OpenGL, I followed [a course](https://vulkan-tutorial.com/) to set up the boilerplate I needed (and introduce me to Vulkan's various concepts). I have to say, I'm pretty proud of it. Like the original TIC-80 project (though I'm not sure if that's what actually ended up on the site), it features randomly placed metaballs, something missing on both the Godot and OpenGL implementation. I also experimented with balls with negative 'size', if size is even the correct term here, and the results were very fun. You can ask for either purely positive metaballs or negative metaballs as well with a command-line option. While I was unable to get the validation layer for a proper FPS count compiled on my Chromebook, I was able to use Mangohud to measure the program's FPS. The results were mixed, with 60 FPS on the default resolution (800x600), and around 30 FPS when fullscreened (1336x768) on my Chromebook. 

### 5: What's next?

I have a few ideas for where I want to go with this project. The obvious one is leave it here. With Vulkan, I've gone about as 'low-level' as I'm willing to go. I might return to [the interpreter tutorial I was working on](http://craftinginterpreters.com/), and I'll obviously continue working on this site. Some other options are DirectX (thought I'm not sure how that would work, as I don't own a Windows computer), and Unreal Engine (again, Windows only makes it a bit awkward since I don't own a Windows computer). 
