Jose Moreno Profile picture
Coffee, math and game lover. Senior Technical Artist at @remedygames. Prev. Alan Wake 2, Metroid Dread @mercurysteam, @sabergames, @labcave and @calendulagame.

Oct 24, 2022, 17 tweets

In this thread we are going, finally, to implement Navier-Stokes simulation in Niagara. We should obtain something like in the gif below. #ue5 #niagara #vfx #gamedev #math

Following the steps in Stam's Stable Fluids paper, let's split our original problem into smaller ones. Basically, we solve the momentum equation (apply forces, advect velocity and diffuse it) and after that we force that computed velocity to be divergence-free using the pressure.

We know how to solve (from the previous thread...more or less) all the elements of last picture, except maybe the "advection" step. Luckily, it can easily solve using a method called "Semi-Lagrangian scheme". Again, I recommend you take a look to @nvidia GPU Gems blog.

Now, we can build the main "structure" of our Niagara emitter.

As usual, we're going to create some parameters and initialize them in the simulation stage called "Initialization", which it's going to run only one time. As usual we use a scratch pad to setup the dimensions of a Grid2D and Render Target, that we'll need in the future.

In the "Compute Forces Constraints" module we set forces that affect the fluid field, like gravity. Additionally, we can set "boundary conditions" (based on static or dynamics object) for the fluid's velocity. It could also be called "Force + Boundary + User Interaction" module.

All quantities in a fluid are transported by its velocity, included, the velocity itself. The module "Compute Advection" implements the "Semi-Lagrangian scheme". This module can be used to transport other quantities like ink or smoke.

The algorithm is based on the "Lagragian viewpoint" of a fluid: the current velocity of a "fluid particle" is the velocity it had in a previous position. This previous position can be estimated using the previous velocity at its current position (figure in Stam's paper).

In this first simulation we are going to leave out the "Blur" module, because we want something with "low" viscosity. In a next thread, we'll implement it.

Now we have a non divergence-free velocity. We need to "extract" its divergence-free component. This is the aim of the next three modules.

The "Compute Divergence" module computes the divergence of the velocity field.

Using the "Compute Jacobi Pressure" we can find, iteratively, the pressure that satisfies the Poisson equation. We can choose different "Num Iterations". As we know, a large number of iterations leads to a better approximation.

Finally, we project the velocity into its divergence-free component using the gradient of the computed pressure in the "Compute Projection" module. This new velocity is going to be the input for the "Compute Forces Constraints" module.

At this point we have a velocity field that follows, roughly, the Navier-Stokes equations. It seems a real flow. We can use this field to drive particles that live in the same Niagara system (reading the Grid2D from other emitter), but (afaik) you cannot share with other systems.

One way to do this is write the velocity field in a Render Target resource, like we did in a previous thread. In this case, we save in the render target the two components of the velocity in the RG channels (modulated to be in the range (0,1)).

This render target "RenderSim", created in the Niagara system, can write an external resource using a "User Parameter".

This is a good point to stop :) In the next thread we'll learn, among other things, how to use this simulation in other Niagara systems.

Share this Scrolly Tale with your friends.

A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.

Keep scrolling