Sand Simulation VFX Breakdown

This post details the breakdown of how we achieve this result and the tricks discovered along the way to create particle physics simulations:

The frame rate stuttering is a result of the GIF file, not the simulation

Above is a showcase of our custom-made interactive application where I am dragging our sand emitter around the screen with my mouse cursor. Sand is emitted and continue to fall, bouncing off objects. We manage to achieve and render interactive frame rates in almost real-time, rendering and simulating 100,000 individual sand grains colliding dynamically with moving and rotating objects, with physics. Our scene has been highly optimized to render within seconds instead of hours, allowing us to freely edit parameters throughout our iterations.

Warning: This post is quite technical until the end where we share how this was achieved:

When you turn off all the lights and make the sand glow

In making this, the main issues to tackle were:

1) Creating believable sand physics (friction, bounciness, spread)

2) Sand colliding dynamically with other 3D objects that are in motion

3) The sheer quantity of particles on screen simultaneously, and keeping realistic render times

First thoughts & Research

Early talks about whether we should do sand simulation in the group wasn’t very positive.

None of us had done it before, nor are we even aware of what techniques we can use to achieve believable sand.

Sand is a complex substance – it is not a viscous liquid in which we can use fluid simulations. Yet at the same time it does not act as a single mold-able solid like clay.

Research on how to achieve this led to pretty complicated techniques and sometimes involve software beyond our understanding. Even in Maya, we had not gone deep enough to understand how to use Maya’s nParticles, which are our guess at how we would execute it in Maya.

I narrowed my search to see if it was possible to create this in Unity. As it is not primarily a simulation software, there were many challenges and question marks.

Thousands of individual sand grains have to be simulated. And what about collisions? Would our sand interact with our gear geometry in the scene? If it had to be done in two separate software, then compositing them together would also be a challenge – we could do sand overlay on the gears, non-interactively, but we had no idea how that would look. If it is too obvious it’s an overlay of two distinct videos, it won’t look good.

I knew that it was possible to use CPU-based particles in Unity that use an underlying physics engine (PhysX 3.4).

But as much as I wanted to use Unity (as our gears are rendered there), the sheer quantity of particles destroyed any hope of this being possible, or is it?

Many youtube tutorials on how to do real-time sand physics within Unity did not achieve the fantastic results we were hoping for. The sand particles were always too big, and too few.

Sand looks more like large spheres in this video
20,000 particles slowly approached the maximum limit of what was do-able in realtime

The truth is, physics calculations consist of plenty of mathematical operations that are expensive to compute in large quantities.

The best simulations went to 20,000 particles, maybe more. But this was still a far cry from believable sand. In other words, we had little luck with tutorials. Bumping it up would exponentially take a toll on simulation times and make it difficult to iterate creatively.

Achieving interactive frame rates was a challenge. But I wanted to see how far we could push it.

I gave all the important gears a Mesh Collider, and enabled collision on particles so they would be added to the physics engine and collide.

I started with a  basic particle system that simulates 1,000 particles.

1000 Particles

Seeing sand interact with the gears for the first tine is cool. But aside from cool physics, the visuals look nasty. How about 10,000?

10,000 Particles

Better but not good. I pushed for 100,000 particles. This was a huge quantity and the simulation slowed down tremendously.  

100,000 Particles

I decided to be crazy to the extreme, and bump it up by a factor of 10 again!

1,000,000 Particles!

The grain sizes were finally fine enough, but at this point each frame was taking one full second to render. It is not too bad, but still extremely sluggish and difficult to experiment with. It was time to see if we could do some optimization.

We are being frame rate-throttled on the CPU, not the GPU, as the mathematical and physics calculations are done on the CPU. Knowing this, all our optimizations are targeted at the CPU-side.

Optimization Tricks

1) Randomizing per-particle properties creates illusion of more sand

The first secret to get fast simulations is to completely NOT simulate sand particles colliding with each other. Instead, we fake it. To help sell the illusion that the particles collide with each other and pile up, we randomize the per-particle properties, indicated in red arrows below.

Our particle collision module settings

Dampen affects how much velocity a particle loses in contact with a surface (aka friction).

Bounce affects how much a sand particle retains its vertical potential energy. High values make each grain act like a bouncy ball.

We randomize them for every sand grain so a sand grain can be heavy, or light, or bouncy, or slidy. This creates variation in our sand behaviour.

This randomization across the particles create higher coverage of sand across the screen, and results in a “spreading” effect lets us use less particle numbers to give illusion of more sand.

2) Using primitive colliders is simpler for the physics engine to calculate

Secondly, we use basic collider shapes.

How collider shape affects physics calculations

Gears have a very complex shape, but when broken down, it is basically a sphere (circle in 2D). In some cases, using multiple sphere colliders is less taxxing than a single, complicated shape collider.

Sphere colliders are easier to detect collision with, as the physics engine only needs to check the distance between two points in 3D space. This makes spheres the cheapest colliders. As our gears are circles from the camera perspective, we use spheres for our gears’ main body instead of cylinders.

We can use both – objects that require precise collision use mesh, while small objects not prioritized in our composition fall back to primitive colliders, and we can even toggle between the collider types through code in the midst of rendering if we really want full control!

Basic colliders give us up to 5 times faster render times. Now we’re talking speed!

3) Restricting physics to a single plane

Simulation is restricted to 2D space, faster computations

We reduce the particle count needed to be simulated by limiting them to a single plane in our 3D space. This way, it is effectively computed in just 2D while being able to interact with our 3D objects. Withour particles overlapping or hidden in the z-axis, it allows us to be as efficient as possible with the particles we emit.

4) Decrease simulation speed

This last trick can double the render speeds but is case specific. By slowing down our simulation speed to 0.5, we effectively only calculate our physics every 2nd frame instead of every frame. Speed wise, this means the CPU performs half as many calculations over the same period of time. However, this can produce undesired effects, such as slow motion.

There was lots of trial and error involved but bit by bit the simulation got faster and was extremely rewarding to watch.

We went from rendering 1 frame per second, needing 1 million particles, to 10 frames a second, needing only 100,000 particles. That is a 10x speed increase in our simulation and render times!

We went from talking about how many seconds we need to render a frame, to how many frames we can render every second. I was personally amazed that we could simulate 100,000 colliding particles at such speeds.

When it became fast enough to render, we aren’t bogged down by render times, so it was time for the fun stuff!

Out of fun, I made the sand glow by giving it emission, and turned off all our scene lights and made the background black.

This created a very interesting composition:

I then wrote the code to make a button that can toggle between both modes in real-time through a single click, as can be seen by the “FX Mode” white button at the top right of the above GIF. This makes it convenient to test compositions while we experiment.

I doubt anyone is interested in the code I wrote (C#), but for the curious:

Function that is executed when FX mode is changed

Additionally, I added a slider that progressively trades off performance for quality for our final render, so we can choose how much sand we want and the quality of the simulation results, based on how much time we are willing to wait for it to render. The longer we are willing to wait for longer render times, the higher the sand particle count. Collision parameters are not changed as this keeps the render consistent regardless of the quality used.

Progressively increasing quality increases sand particle count and render times, while inversely decreasing sand grains’ size

This is done by writing a script to set our particle sizes and emission count. Again, for anyone who is interested to look at the script to achieve this:

I also wrote another script that reduces the particles used during edit mode, and pumps up the particle count only during our rendering. This way we can iterate even faster with less lag in the scene view during our iterations, and not have to manually remember to increase the final quality before we render.


Author: Yeo Ying Zhi

I like to develop games.

Leave a Reply