Snow Simulation Final Project

CS 184: Computer Graphics and Imaging, Spring 2018

Yue (Andy) Zhang, cs184-acv, SID: 25116308

Brandon Huang, cs184-afi, SID: 25523652

Ankit Patel, cs184-agy, SID: 25249884

Github Repository


Abstract

We implement 3D snow simulation using the material point method (MPM) and a renderer in OpenGL. Snow is extremely challenging to simulate because it has material characteristics of both liquids and solids. The MPM simulation approach is implemented based on a 2013 paper by Stomakhin et al., and offers a reasonable runtime and control over some intuitive properties of the material. We show that plausible results can be obtained even with relatively coarse particle and grid resolution, and replicate some of the demonstrations from the original paper. We also implement basic 3D rendering of snow scenes, and iteratively implemented optimizations to improve simulation runtime.

Technical Approach

Overview of MPM. Particle mass and velocity are interpolated onto a grid, where most physics computations occur. The velocities at the next time step are interpolated back onto the particles.

There are many ways to simulate snow, each with strengths and weaknesses. Mesh based methods have significant trouble modeling fracture, because of the need to explicitly define fracture of a contiguous solid. Pure grid based methods struggle with plasticity because deforming a grid results in a great deal of nonlinear computation. And particle-only methods suffer from inefficiency due to a high demand for nearest neighbor queries to compute dynamics. The material point method is a simulation method well-suited for continuum materials. MPM addresses these problems by using the grid as a "scratch-pad" for computations, while maintaining Lagrangian state as point particles with mass, volume, and velocity. MPM is mesh-free and avoids directly computing interactions between particles by interpolating particle properties onto a 3D grid, a computes physics on this grid.

Physical Simulation

Our simulator defines as primitives snow particles and rectangular/planar interactable collision objects. Our implementation of MPM closely resembles the implementation provided in the paper. Here is an overview of our approach, numbered consistently with the diagram above:

  1. Interpolate particle states to the grid. We use a cubic b-spline to interpolate the mass and velocity of each particle onto the 3D grid. Each particle affects multiple grid cells, in its neighborhood.
  2. Compute the volume and density of each particle. This is done once in the entire simulation. The density of each grid cell is computed, and this is interpolated to particles using the same b-spline weights as part 1.
  3. Compute grid forces. Forces are first computed on each grid cell based on the elastic potential energy of particles in its neighborhood.
  4. Compute grid velocities. The velocity of each grid cell can be computed from its force.
  5. Compute grid-based collisions. If the grid velocities cause particles to collide with any rigid objects, adjust its velocity so it does not pass through.
  6. Perform semi-implicit numerical integration. We skipped this step because we found this step to be quite difficult to understand, and instead, used explicit numerical integration from class.
  7. Update the deformation gradient of each particle, which affects particle velocities indirectly.
  8. Update particle velocities. From the grid velocities computed earlier, a weighted average of PIC and FLIP updates are interpolated to particle velocities.
  9. Compute particle-based collisions. Grid-based collisions are not entirely accurate, so we catch any improperly collided particles in this step.
  10. Update particle positions (finally!).

Rendering

We decided to focus on physics over rendering for this project, and approximated the appearance of our snow by rendering the particles as translucent cubes. This gives us the ability to see how densely the snow packs, while avoiding the overhead associated with rendering using a volumetric pathtracer, which is the suggested method.

Our rendering is done using OpenGL primitives. We populate the VBO and VAO appropriately for a few types of objects, notably cubes, planes and grid lines.

Optimizations

Simulating snow with a large number of particles is extremely computationally intensive, even using a relatively efficient method like MPM. We found our code to be prohibitively slow on first draft, and took numerous steps to speed it up. Some examples:

  1. The B-spline interpolation is used for all operations transferring properties between the particles and grid. Using a C++ profiler, we discovered that computing this B-spline was taking a large proportion of the runtime. Since the B-spline weights and gradients are reused several times in one update step, this saves time.
  2. Even computing the B-spline weights once per particle per update was expensive. To mitigate this further, we observed that the scalar B-spline function is nonzero only over the domain [-2,2]. We thus save time by precomputing, at the start of the program, a discretization of the B-spline (perhaps 10,000 values), and looking up interpolation weights at the start of each update step.

    An example of discretizing a function. Computed values can be stored for later lookup.

  3. Another optimization comes from the insight that the grid serves only as a scratchpad for the particle computations, and only need to be modeled when a particle is nearby. Originally we computed the simulation over all of the grid nodes. This proved to be quite inefficient as the number of empty grid nodes generally vastly exceeds the number of particles. For example, when using 5000 particles on a 50 x 50 x 50 grid, there are 125,000 grid nodes. At most 5000 of them can actually contain a particle, and generally since the particles are clumped, the number of filled grid nodes is considerably lower. To resolve this, we keep a set of "in use" nodes, and only work with nodes within the particles' interpolation radii. However, the problem arises that as the simulation progresses, particles move away from some grid nodes, which become unused. To resolve this, we clear out old grid nodes periodically during the simulation.
    Sketch of material points (particles) on an Eulerian grid. The empty white grid cells are a target for optimization.

  4. The largest speedup came simply from using the cmake flag CMAKE_BUILD_TYPE=Release. It seems a lot of C++ optimizations are nicely built in to the compiler. We achieved about 10x speedup this way.
  5. We attempted to use thread pools to parallelize our computation, but ran into issues with critical data. We had trouble installing OpenMP on OS X, and so only had access to low-level threading primitives without easy-to-use abstractions.

Miscellaneous

Our implementation also provides a way to save simulations as videos. In addition to the live rendering, the program optionally saves the rendered frames into an .avi video file. This is especially useful for rendering at high grid resolution to improve physics accuracy, where each frame takes a few seconds or minutes to compute.

Problems

The biggest problem we encountered was runtime efficiency. Our initial implementation focused on getting everything to work, but was very inefficient, so after the initial implementation, we spent a lot of time rewriting the code for efficiency, as described in the "Optimization" section. With greater optimization came increasing complexity, as we discovered/introduced weirder and weirder bugs in our code over time.

Another major challenge was CMake, and integrating/compiling the various libraries we used. This was our first time working with CMake. Each library seemed to have a slightly different way of integrating, and given our limited experience, figuring it the integration involved much trial and error. Furthermore, we were entirely unable to compile our project on Ubuntu machines (e.g. home desktops, instructional machines) due to system-specific compilation issues.

Lessons Learned

Starting this project, we worried that it would be next to impossible to debug our simulation code. Since the individual steps don't have a very interpretable or testable effect on the particles, we figured it would be hard to tell if our computations were accurate. Even visualizing something like the grid forces would yield vague insight at best without additional expertise. Ultimately, what we are all intuitively familiar with is the visualized dynamics of snow, so we eventually were forced to simply visually inspect our results for debugging. This ended up being easier than we thought; some bugs exhibit quite obvious patterns when visualized, especially when the simulation quality is reduced to allow for real-time simulation for debugging purposes. Between this and careful proofreading of the code, we were able to catch all the bugs. In the future, if the source materials provide clear explanation (which the Stomakhin paper generally did), we can be less wary to implement our pipeline end-to-end.

Despite our optimizations, we were not able to scale up our simulation to reference numbers given in the Stomakhin paper. However, we were still able to see some plausible snow dynamics. This is a good reminder that in graphics, one's work does not have to be perfect to produce valuable results.

On the other hand, in terms of frameworks, setup and system specifics, we underestimated the degree to which problems would arise. This may partly be the result of us starting our project on a barebones basis, and may also be partly because graphics code appears to be very beginner-unfriendly. There doesn't seem to be any way around this other than just getting good at it, but good to keep in mind.

Results

We demonstrate some of the simuluation results in this section. Note that in all demonstration videos, there exist transparent walls on the four sides of the scene area, and snow particles will collide and stick to these walls, despite being transparent.

There are four main physical parameters that control the material property of the snow, and by tuning these coefficents, we can create different types of snow. The hardening coefficient and Young's modulus determines how fast snow breaks apart, and controls whether the snow is brittle/icy or ductile/watery. The critical compression and critical stretch are two parameters that determine when snow breaks apart, and whether snow is chunky/wet or powdery/dry.

Default
Low hardening
Low Young's modulus
Low critical compression
Low critical stretch
Low critical compression and stretch

We'll also demonstrate how snow interacts with rigid objects. Here, a rigid cube "cannonball" hits a tower of snow at two different velocities.

Low velocity collision
High velocity collision

Here is a "snowplow" throwing up snow.

Snowplow

Here, a snowball crashes into the wall. Note that upon collision, a portion of the snowball fractures and falls off, while another portion sticks to the wall and falls off slowly.

Snowplow

Contribution Breakdown

Ankit bootstrapped all of the OpenGL setup (shaders, VBO/VAO/basic drawing stuff) as well as wrangling with GLFW. He implemented most of the user/GUI features e.g. camera panning/movement, and also discovered and/or wrote many of the simulation optimizations. Finally, to his great disdain, he also tackled a great many CMake-related issues.

Andy set up the libraries and the first 3 steps of the physics simulation using GLM and Eigen, including the initial code for interpolation, SVD, etc. He also implemented several features in collision code, set up test scenes, and determined good demos for said scenes by tweaking simulation parameters. Finally, he worked on the milestone and final submission deliverables.

Brandon wrote the rest of the physics simulation and optimized grid node usage. He also took care of refactors to the Grid class, wrote collision code, set up test scenes, and wrote sampling routines to populate snow objects with randomly distributed particles. Finally, he also worked on the milestone and final submission deliverables.

References

  1. Stomakhin's original paper, 2013: http://alexey.stomakhin.com/research/snow.html
  2. OpenGL tutorial: https://learnopengl.com/
  3. GLAD: https://github.com/Dav1dde/glad
  4. GLFW: http://www.glfw.org/docs/latest/quick.html
  5. GLM: https://glm.g-truc.net/0.9.8/index.html
  6. Eigen: http://eigen.tuxfamily.org/index.php?title=Main_Page
bLoOpErS