Dynamic flowing foam in Unity

In this article, I’ll explain how to achieve a dynamic flowing foam in Unity. This article is not a step-by-step tutorial but more of a general guideline of how to achieve the effect. I’ll be using URP, but this technique can be extrapolated to any rendering pipeline. This effect is based on this awesome SIGGRAPH presentation. So check it out if you want to know more about this it. You Can find the source files at the end of the article.

Setup

Assuming that you already have a URP project, add a plane and a bunch of mesh renderers into the scene.

Dynamic Foam Renderer Feature

First, we need to create a Renderer Feature called “DynamicFoamRendererFeature”. A Rendererfeature allows us to extend the rendering pipeline. They are very flexible and will enable us to decouple the rendering logic from a particular scene. We will set the renderPassEvent as BeforeRenderingOpaques so our texture could be sampled by opaques objects as well.

The first thing that we need is to store the depth difference between the plane and the objects that the plane intersects. We can achieve this by creating a custom shader pass that will compute the depth difference and render it into a custom render target.

In the code we do this by getting a render texture and setting it as the render target calling ConfigureTarget. Note that the render target identifier and the render texture identifier must be the same for them to be linked. We also need to release our render texture inside the FrameCleanup method.

Then we need to execute the render feature. Here is where we will render our custom shader pass where the depth difference is supposed to be rendered. We can achieve that by creating a DrawSettings and passing our shader pass identifier to it. Then if we call DrawRenderers passing our draw settings we will render all the objects that have our custom shader pass on its shader.

Finally, we set our global texture passing our render target identifier to debug it later.

Depth difference Shader Pass

Here is where the magic happens. We need to map the plane into the screen using its UVs as clip position. This way, we can effectively output its fragment into our render target (Remember that we are using the camera render target descriptor in our custom render target). Still, we also need to compute the vertex clip position to calculate its depth relative to the camera. The lightmode tag must be the same as the ShaderTagId set on our custom render feature.

Then we can sample our foam texture inside the forward pass of our shader.

Flow

The next step is to make the foam “flow” We can achieve this by two additional render textures, one to store the previous state of the flow and the second to store the current state. I’ll use a compute shader to apply the flow but this could also be performed on a fragment shader.

  • Sample the current depth from the DepthDifference texture
  • Create a flow vector proportional to the texture size (Here I hardcoded (1,0) as the flow direction for simplicity)
  • Sample the previous foam from _DepthFoam_Read
  • Set the current foam in _DephFoam_Write equals to the current depth plus the displaced foam.

Then we need to initialize our read and write textures. We can initialize them inside the Configure method. (Note that we will need to manually dispose of these textures once we don’t need them anymore)

After that, we need to pass these textures to our compute shader, execute our kernel, and then swap them at the end of the frame. We can do this by calling SwapFoamTextures inside the FrameCleanup method.

Finally, we need to set up our render pass in our renderer data.

The effect is mostly done. All that is left is to make it look pretty.

We can add a nice distortion using a distortion map

Now it’s looking more interesting but it can looks way better if we apply some blur on it. I just lazily made some adjustments to Ronja’s Blur Postprocessing Effect tutorial

That’s all! I hope this to be useful to you c:

Source Code

https://github.com/JoseMiguelPizarro/DynamicFlowingFoam