Container With Liquid (UE)

For one of the game projects I work on (together with splendidly lovely people, not just me alone), there’s an enemy type that swings a big container. The container is supposed to be filled with liquid. Originally that was in the texture, but then of course the liquid never moves.

This is the container retextured without any liquid inside.

Problem

The liquid should move when the container is rotated. Additionally, something should happen inside the liquid, so that it doesn’t look simply like a single color. Lastly, it should fit the more pixely aesthetic.

These would then be the goals:

  • Bubbles should rise from bottom to surface vertically even when container is rotated
  • Liquid surface should be horizontal even when container is rotated
  • generated liquid texture should be pixelated

Pixelate Fragment Shader

Because everything was to be pixelated, I needed to find a solution for that. It took me quite a long time to come up with one that would work when the object is rotated. Pixelating a texture is easy enough, but pixelating something generated in a fragment shader turned out to be a little bit more difficult. Where do we define the pixelgrid? It had to be in localspace so that the pixel grid stays the same no matter the tranform of the object.

Here is the base for the pixel grid. The core is an Fmod node and transforming this into worldspace. (Subtracting -500 didn’t make it in the final shader, no worries). The idea is to use this grid later to subtract it from positions.

Bubbles

For the bubbles I used the worldspace position and moved it upwards. After that, it was important to scale that to fit the grid / pixel size, so that 0-1 is just as large as one pixel. In order to actually pixelate this, I subtracted the previously made pixel grid from that. Lastly, I multiplied it with a smaller number to scale the generated noise. This noise got then adjusted to look more like bubbles and the result got colored with a simple lerp.

Liquid Surface

The surface uses the same pixelation technique as the bubbles. The container has its pivot at the bottom. In order to have the container half filled, I wanted to get the center. That was done by transforming an up-vector from local space into world space and adding it to the pivot location. Now, if we multiply this vector with different values, we get different heights and therefore different fill amounts for the liquid in the container.

From this I subtracted the worldposition, scaled it to the pixelsize again and subtracted the pixelthing from the very beginning again. The only thing needed from this was the Z value.

Combining Everything

In order to combine everything, a second texture was created to mask the glass parts of the container. This masked was combined with the liquid surface mask. The liquid itself was multiplied with the albedo texture and then masked.

Result

So what’s the result of all this? Why the weird setup with localposition and worldposition as opposed to taking the UV coordinates and working with those? The bubbles now rise upwards and the liquid surface stays horizontal, no matter how the object is rotated.

With that, there is only one thing left to do. And that is to view the whole shader combined with the enemy and the particle effect.

Conclusion

What would I do different now? Not that much, to be honest. I will refine it a little bit, and see where I could improve performance. The main thing I would like to add is a second noise with which to control the first noise for the bubbles. I am hoping that this way I can make the bubbles not go exactly vertically up, but let them slide left and right a little bit as well to make them feel more dynamic. Other than that, I am just happy I figured out how to pixelate it well, because this was the real challenge for me here.

Obviously it would be amazing to have some type of inertia for the liquid surface so that it isn’t always exactly horizontal, but that is for another time.

Leave a comment