CS 184: Computer Graphics and Imaging, Spring 2021

Final Project: Fluid Dynamics

Adnaan Sachidanandan (3033810069), Lily Yang (3034551742), Mark Lindblad (3033936260), Sophia Song (3033835419)

Splash onto bounded surface

Abstract

In order to digitally recreate real-world scenes for video games, movies, AR/MR/VR and industrial simulations, creators need visually convincing representations of fluids. Scientifically accurate simulations governed by complex equations are true to reality, but difficult to implement and are computationally expensive to use. In the following project, we explore the implications of speed, interpretability, and interactivity of fluid simulations for viewers and programmers. Our mathematical implementation is based on Muller et al.’s Position based dynamics (2007) and the follow-up paper Macklin and Muller’s Position Based Fluids (2013), which involves understanding the physics of individual particles and calculating particle positions from density and other parameters. We modified Project 4 rendering GUI to support particles instead of cloth grids and extended functionality to allow for three-dimensional grid parameters for more complexity in simulation. To execute fast nearest neighbor search, we utilized the nanoflann library during our intermediate calculations. Although we were not able to implement a water shader and the machine learning speed-up that we hoped for, we believe our results in creating a real-time renderer that only uses CPU computation aligns to our original goals for speed and interpretability.

Technical approach

Extending and modifying the Project 4 GUI

Before implementing the specifics for fluid simulation mathematics, we prepared for integration to our current system via an object-oriented approach and interacted with pre-made files from Project 4. We changed functionalities to provide flexibility toward creating different .json set-up files and to create applicable visualization of individual particles.

Modifying the PointMass class, Cloth.cpp, and associated files

Our two main goals here was to provide increased alignment to variables introduced within Position Based Fluids (2013) and new methods of handling particle rendering by the Project 4 GUI. To respond to the first point, we added in Vector3D dp, Particle *particle, double lambda, Vector3D velocity_star, and Vector3D position_star to follow the same attribute requirements from the paper. For the second motive, we created a Particle class to allow for quick renders between simulation steps by avoiding re-instantiation of sphere meshes that represent each particle. This was then associated with our PointMass objects through the buildGrid method, as we assigned each new particle to each PointMass.

3D VOLUME GRIDS: Furthermore, we allowed for a 3D volume of particles to be created, as opposed to a 2D grid done in Project 4 to more accurately simulate fluids, since fluids are not realistically represented by thin sheets.This was accomplished through adding a third attribute num_depth_points and an extra loop over the buildGrid method. To center our 3D particle volume, we offset the spacing by subtracting half the number of points along that dimension. Dynamically calculating the spacing through dividing the dimension attributes by the number of points, we kept constant spacing across each respective dimension.

Previous cloth 2D implmentation
Modified 3D support

COLOR-CODED SPHERE RENDERINGS: To allow for visual communication of particle position, we varied the color depending on the Y positions of each particle. We modified the Default.vert, Phong.frag, and Wireframe.frag files to include color attributes and changed the sphere_drawing.cpp file to provide the colors to our spheres.

Each sphere was color-coded based on Y position

CREATING NEW SCENES: We wanted to be able to create different surfaces for our particles to interact with. We modified the way that .JSON files were parsed to have more control over what scenes we created. The two scenes we ended up making was 1. a 4-walled room and 2. a slanted alley to provide fluid particle collisions with containers.

Box container JSON scene file

Implementing mathematical particle behavior via Position Based Fluids (2013) Macklin & Muller

The algorithm described by Macklin and Muller relies heavily on identifying the neighboring particles near a given particle to compute its next position. Naive search throughout all points is inefficient and antagonistic to our goal of achieving fast rendering, so we looked into clustering methods to improve performance. At first, we experimented with spatial hashing that we completed in Project 4, but found it to be extremely buggy, as grid lines and regularities in particle movement resulted. Via the suggestion of TA Utkarsh, we utilized nanoflann, which finds nearest neighbors using a KDTree approach with point clouds utilizing a search radius.

Utilizing nanoflann to allow for quick Nearest Neighbor identification

In Cloth.cpp, we begin building our point cloud to search for nearest neighbors, creating a vector of PointMasses pmNeighbors to hold the list of neighbors that correspond to a PointMass object in row-major order. We utilize nanoflann’s radius search function to extract neighbors and prepare for usage in the following algorithm components.

Accounting for density and constraint calculations

Position Based Fluids provides an approach that does not require direct force manipulations, and utilizes position changes to implicitly convey the forces at play. This is helpful with overall stability and density issues from previous work with Smoothed Particle Hydrodynamics (SPH), Monaghan (1992, 1994). The following set of equations make sure density is constant across the movement of the particle fluid system.

After finding the associated neighbors of each particle during each timestep, we proceed to implementing the equations above, which describes the density of a particle and its corresponding density constraint C_i . This step involves the Poly6 kernel, W, which is as follows:

Function W(Vector3D r, double h)
if 0 < norm(r) < h:
     Return 315.0 / (64.0 * PI * pow( h, 9.0)) * pow(h * h - norm(r)  * norm(r) , 3);
Return 0;

Next we account for neighborhood density corrections, lambda. This is different from the individual particle density constraints in that it provides context to other particle interactions. We utilize the Spiky kernel gradW to evaluate the denominator and added an epsilon, 1e-5, term in the denominator to prevent instability. The Spiky kernel is as follows:

Function gradW(Vector3D r, double h)
if 0 < norm(r) < h:
      Return -45.0f / (PI * pow(h, 6)) * pow(h - norm(r), 2) / (norm(r) + 1e-5) * r;
Return 0;

Finally, we used this quantity to account for the total position update of a given particle, after adding in a corrective term to prevent issues associated with clustering of particles.

Accounting for motion coherence and energy conservation

XSPH Viscosity
To preserve coherency of particle motion, we implemented the XSPH viscosity referencing Schecter and Bridson (2012). This involved an additional loop over our particles and calculating the xsph, which was added to each particle’s velocity after a being scaled down by c = 0.001.
Vorticity confinement

To account for turbulent motion and energy conservation properties of simulation, we implemented vorticity confinement for replacing lost energy.

We calculated the vorticity element at a given particle’s location and then applied a corrective force to the forces of our current particle and updated its respective velocity

Putting the parts together in our simulate function

Each of these quantities were then integrated into our overall simulation step. We utilized a set amount of simulation steps and in each loop, we do the following:

Accumulate lambda values through iterating through point masses and their neighbors
Calculate delta p_i, the change in position for each particle through their relationship to their neighbors
Update position_star with delta p_i
Check for collisions with external object
Update velocity based on current position_star and existing position
Account for viscosity and vorticity confinement for velocity updates
Update positions

Problems encountered

  1. Resolving strange behavior along the edges of the prisms for position-based hashing:
    Initially when we utilize spatial hashing we get the result above, where a grid of particles start to form. We believe this was due to the inaccuracies surrounding the process of finding neighbors. Since the spatial hashing implementation from project 4 relied on a more grid-based approach to split up 3D space, it may have led to inconsistent and incorrect behavior along the edges of the space. This is reflected by the grid-like organization of the particles as they hit the plane.
    We resolved this issue by finding a more robust nearest neighbor implementation through the nanoflann library as described in this section. Since neighbors were now found through a spherical/radius-based approach, it would resolve the issues we had from using hashing which would be limited by the cubic space.
  2. Isolating errors within highly dependent segments of code:
    Much of the mathematical equations needed to be implemented relied on previous parts being implemented correctly. This created a domino effect when one segment was faulty. A side effect of this dependency was that it was difficult to unit test the functions to ensure correctness at each step.
    Our main approach to resolving this issue was through CLion debugging tools with strategic print placements and break points, while allowing for as much abstraction between each step as possible to make the code segments more independent.
  3. Maintaining efficiency with more particles and complex computations:
    Though our simulation works in real-time at smaller scale particle renderings, its ability to handle scale was an issue. We responded to these issues with localized optimization. First, we identified the bottleneck of nearest neighbor searches and utilized packaged KD-tree speedups to reduce complexity. We also kept most added-on rendering quite simple to reduce the impact of features on runtime. Finally, we removed any unnecessary computation and memoized any repeated calculations.
  4. Time limits when approaching an ambitious proposal:
    Our original idea involved components of water shader rendering and machine learning capabilities. One of our main setbacks was the amount of time it took to debug the baseline fluid simulation. From both a logistical end and a theory end, making the simulation work and ironing out the details to integrate with the GUI took much longer than we thought. We decided not to focus on any subsidiary features like aesthetics (shaders) and speed (machine learning) and wanted to prioritize the realism of the simulation, since the core of our goals was to accurately display a fluid simulation and present it at a reasonable speed.
    Though we could not deliver the moonshot goals of our project, we were able to display significant growth from our midway check-in. Through multiple debug sessions and modifying current resources, we were able to provide the realism at a speed that we originally set out for.
  5. Working online and coordinating/delegating tasks among 4 team members:
    Issues with version control and maintaining consistency between our code, especially online, was a major challenge we faced.
    We combatted this by utilizing Liveshare Coding, allowing simultaneous editing on the same document as a team. We also consistently opened up Zoom meetings and work sessions to allow for collaboration, despite the time constraints that made a 4 person meeting more logistically difficult.

Lessons learned

  1. Natural phenomena are intriguing and society often takes it for granted. Throughout implementing the fluid simulation, we were able to gain a greater appreciation and understanding of the physical world around us. We interact with water and other fluids so often that we forget how amazing for it to simply exist, move, and appear the way they do. After these couple weeks of reading papers, debugging, thinking critically about what nature does, we saw the value in recreation and curiosity.
  2. Complexity can be reduced when thinking about what features are subsidiary. It is especially easy to think big, and often that is outside of the constraints that time and resources may yield. Prioritization and abstraction were major skills we strengthened throughout this project as we isolated what were the core things we needed to deliver and what were simply nice-to-haves.
  3. Having a guide does not guarantee that the process will be easy. Though most of our work was based on the Position Based Fluids (2013) paper, and most explicit equations are steps to implementation were readily available, the complexity of putting things into practice was challenging, but fulfilling. We learned how to adapt when we faced constraints and how to modify approaches to fit what we wanted to see.

Results

Below are our results. The first video displays a stream of fluid flowing into a box-like container. The second displays the motion into a narrow alley. Our renders could be accomplished in real time, with 1440 points rendered with various colors at 60 frames per second.





Presentation video

Presentation Slides





References

Macklin, Miles and Matthias Muller. Position Based Fluids (2013). NVIDIA.
Muller, Matthias, David Charypar, and Markus Gross. Particle-Based Fluid Simulation for Interactive Applications (2003). Department of Computer Science, Federal Institute of Technology Zurich (ETHZ), Switzerland.
Muller, Matthias, Bruno Heidelberger, Marcus Hennix, John Ratcliff. Position Based Dynamics (2007). ScienceDirect.
Schechter, Hagit and Robert Bridson. Ghost SPH for Animating Water (2012). ACM Trans. Graph.

Contributions

Adnaan Sachidanandan

Adnaan worked on lots of the core functionality in the codebase. He helped implement the 3D initialization of fluid particles and box scenes. He also worked with Lily to implement the incompressiblity constraint and all of its equations, and he implemented tensile instability. Adnaan also worked with shaders to develop the height-based coloring of particles in the final renders.

Lily Yang

Lily worked with Adnaan via the CLion coding Liveshare platform to implement the initial functionality of the paper, putting together the skeleton needed for calculating density, confinement coefficients, and neighboring particle interactions. She also contributed to modifying the box scene and prototyping the 3D support in the buildGrid function. Lily was responsible for creating the majority of the slides for the milestone and final deliverables, and wrote the final writeup.

Mark Lindblad

Mark worked with Sophia to implement and debug viscosity and vorticity confinement over Visual Studio live share. Mark modified the json scene interpreting code to support scenes with multiple collidable planes. He also helped write the project proposal and the milestone report. In collaboration with Lily, Mark created two box scenes to contain the simulated fluid.

Sophia Song

Sophia worked with Mark to implement viscosity and vorticity confinement over Visual Studio live share.