This page is for showcasing my SPH Fluid Simulation.
Intro
Fluid Simulations
What even is a fluid simulation? At its most basic, a fluid simulation is a
way of visualizing the movement of fluid. Fluid sims can vary a lot in terms
of style and complexity. For this project, I was specifically focusing on
physically-based fluid simulations as opposed to something more cartoon-y.
This type of approach is called Computational Fluid Dynamics (CFD) because as
the name implies we are computationally measuring the movement of the fluid
using approximations of real-world physics. To accomplish this, I used the
smoothed-particle hydrodynamics (SPH) approach.
Eulerian vs Lagrangian
Computational Fluid Dynamics discretizes space in one of two ways: Eulerian
and Lagrangian. The Eulerian approach is also typically called the 'Grid Method'
since it relies on partitioning space into a grid/mesh, where we calculate and
track the velocity at fixed grid locations. The Lagrangian approach does not
use a mesh, but instead tracks the position and velocity of individual moving
particles in space, relying on calculating the effects of external forces,
viscosity, pressure field changes, and more. Both approaches have their own
pros and cons, and after weighing the choices I ultimatley chose to go with a
Lagrangian approach like SPH.
Goal
I have always been fascinated with widgets like this.
I wanted to create something similar (i.e. a particle-based representation of
fluid, interactivity via clicking and dragging the mouse, and collision detection
with boundaries of the container.
Credit
I'd like to give credit where credit is due. I couldn't have done a huge part of this
project without the wonderful resource that Lucas-Schuermann provided on his webite
where he detailed his approach to the problem. You can find it here.
You will notice some similarities between his project and mine, since I used his
approach as a template for my work. However there are differences between our code,
both in terms of implementation and libraries used.
Architecture/System
Libraries/Language
For this project I used C++ with OpenGL. One of my reasons for choosing this project
was to get more familiar with OpenGL, since I felt I only got a brief introduction to
it in prior courses. I used C++ because it's the language I'm most familiar with. In
order to do efficient matrix operations, I used the Eigen library. To help manage
OpenGL windows I used the GLFW library, and I also used some of the OpenGL extensions
with GLEW. In order to parallelize some of my code, I used OpenMP.
Functions/Methods
After reading this paper
I set out to start implementing the equations listed under section 3 of the paper,
Modeling Fluids with Particles. This paper had the equations needed to account for the
pressure, viscosity, surface tension, and external forces on a given fluid particle.
However, upon implementing this I realized that I needed to find a way to integrate,
so I decided to go with Euler's Method, similar to what Lucas-Schuermann did. However
even after doing all this, my particles were bouncing around like crazy with way too
much velocity. I eventually realized this was becuase the smoothing kernels that the
paper used were for 3-dimensional fluid simulations, whereas mine was a 2D one.
Fortunately for me, Lucas-Schuermann had my back again and documented the specific
constants he happened to use for his 2D fluid simulation. This was what got the ball
rolling for me, as I now had a decent (but more importantly working) fluid simulation.
Particle Processing
At the start of the code after initializing the window, I create a grid of particles
above the lower boundary of the container. The goal is to see how the fluid particles
crash against the boundary and disperse. At each time step the udpate() function is
called, which calculates the various forces applied on each particle at that time,
before figuring out how the particle should move for the next time step. This updated
information then gets sent to my OpenGL shaders which process the new positions,
resulting in the animated effect.
Advanced Architecture
What I have described so far is very similar to the Lucas-Schuermann implementation. I
wanted to go a bit beyond what was described in the article and represented in his code.
To do this, I implemented 2 optimizations and added 1 new feature.
The first optimization
was in the form of parallelizing the for-loops in the particle force calculation using
the OpenMP library. Parallelizing this code was not a challenge, since at each time step
we are calculating how a particle should move based on the orientation of all the other
particles at that time step. Since we are not worried about particles after the time step
affecting the calculations (i.e. there is no order to how they should be calculated), this
is a situation where race conditions will not occur, we can simply calculate how each
particle moves in parallel threads. The second optimization had to do with partitioning
the space into a spatial grid to help with neighbor calculations. The way the smoothing
kernels work is by weighing the contributions from all neighbors within a certain
distance from a particle. Previously I was looping through every particle in the scene,
determining whether they were at the right distance, and then applying the kernel.
This is very expensive, as we have to loop over every other particle FOR every particle.
To solve this problem, I took a spatial hashing approach, outlined in this article.
This approach was the key change needed to speed up my slow fluid simulation.
In addition to these 2 optimizations, I also added a feature I didn't see mentioned anywhere
in the Lucas-Schuermann article. As I mentioned previously in the document, I was heavily
inspired by those simulations where the user could drag their mouse to add a force to the
particles. In order to implement this feature, I keep track of whenever the user clicks
their mouse inside the application window. After that, they can move their mouse whereever
they want to, but only the FINAL destination of the mouse upon release is used. This of
course means that the forces that the user can apply on the particles must be in a straight
line. The difference between the start and end position is used to create a force vector
which gets added to the particle. This was by far the most satisfying change for me, since
it meant I got to play around with the fluid simulation like I always wanted to.
Results
Fluid Sim with 200 Particles:
Fluid Sim with 400 Particles (Slower):
Mouse Drag in Fluid Simulation:
Future Work
Implement surface tension
Generate a Fluid Simulation in 3D
Add implicit integration (such as the Leapfrog Method) to reduce parameter sensitivity
Adaptive Time Stepping (only use smaller time steps when needed, like high velocities or density changes)