top of page

Champagne

Spring 2025 VSFX-428 Final Project

​

FLIP - Iteration #6 - 05/25/25 - 05/27/25

With little time left remaining in the quarter, I focused my last efforts on improving the lookdev aspect of the project. This proved to be quite a challenge getting all elements to work well with one another, so I went layer by layer to make sure the shaders held up both individually and as a whole.

Main fluid

Large Bubble

Small Bubbles

Ground plane gradient based on Y

Glass with subtle roughness

Foam volume at top

Fluid alone

Fluid with volume

Secondary bubble elements alone

Fluid with volume and secondary bubbles

While there is definitely more work needed on the lookdev, and likely more work in simulation/meshing as well, this was a great introduction to fluids. I can start to see how larger scale simulations may need to be organized, how to pull groups of particles based on certain conditions, etc. I may continue working on this or start a new fluid project altogether- we shall see!

FLIP - Iteration #5 - 05/23/25

Today, I wanted to adjust the third set of bubbles I made in the previous iteration. The previous setup kept points based directly on point number, which creates a flickering effect as the total point count fluctuates. This should instead be based on the ID attribute, which remains the same throughout the particle's lifespan.

​​

This third layer of bubbles should spawn directly below where the liquid is being poured, giving the correct effect of carbonated fluids. To replicate this, I use a wrangle node to isolate points in the foam group based on age and vorticity. If the particle age is less than the desired amount, and falls within a given vorticity range, that point's ID attribute is appended to an array called id_list. Then, if a given point's ID attribute is NOT in this array, the wrangle will delete it.

​

In a previous iteration, I had created a volume group that was based on the density of the foam-- also following Nine Between's tutorial. However, this is only really convenient when there is a lot of foam particles rising to the top. In my case, I found it was a better approach to use the foam's bounding box and scrape off the top layer to apply a volume.

​​

Using these two isolated groups, I create new layers of bubbles and volumes. For the volume, I use a VDB with the glass collision subtracted out, then for the bubbles, I set the particles' pscale and use a boolean to exclude the glass.

Test 1 - all elements compiled

Test 2 - added layer of bubble at surface

Test 3 - increased bubble quantity

With the separate elements now compiled and brought into Karma, I created simple procedural shaders and began rendering the project out.

FLIP - Iteration #4 - 05/20/25

Continuing from last work sessions, I focused on adjusting the simulations to minimize the volume loss issue. One approach I thought could work was to set the foam group's lifespan back to 100 after the fluid is done pouring into the glass. The foam particles are set with lower lifespans as the bubbles pop or dissipate into the fluid, so without a continuous flow, volume loss is expected.

​

In a POP Wrangle, I set the frame number for when the pour ends, and identify the maximum lifespan for the foam. If the pour has completed, and the point does not have the maximum lifespan (aka the foam rising and sitting on the top of the fluid), the lifespan will be set to 100. This prevents some of the foam from dying after the pour is complete.

Next I began running more tests to see how the residual foam would be affected by the wrangle. Through the tests, I noted that the parameters effecting the amount of foam, the birth and death rates, and the particle separation, play a large yet subtle role in volume loss. I simulated about 15 variations before landing on a simulation I could continue working with.

​

​

​

Test 14 -

​​​

lingering foam max lifespan = 7

​

0.15 velocity

​

foam group vorticity > 460

viscosity 0.08

​

birth threshold 0.4

death threshold 1.3

​

3 substeps

0.002 particle separation

0.95 particle radius scale

0.8 grid scale

0.65 separation rate

​

​

Test 15 -

​​​

lingering foam max lifespan = 7

​

0.1 velocity

​

foam group vorticity > 460

viscosity 0.08

​

birth threshold 0.5

death threshold 1.1

​

3 substeps

0.002 particle separation

0.95 particle radius scale

0.8 grid scale

0.65 separation rate

​

​

Test 16 -

​​​

lingering foam max lifespan = 5

​

0.05 velocity

​

foam group vorticity > 450

viscosity 0.08

​

birth threshold 0.3

death threshold 1.1

​

3 substeps

0.002 particle separation

0.9 particle radius scale

0.8 grid scale

0.5 separation rate

​

​

Test 17 -

​​​

lingering foam max lifespan = 5

​

0.05 velocity

​

foam group vorticity > 450

viscosity 0.08

​

birth threshold 0.5

death threshold 1.1

​

3 substeps

0.002 particle separation

0.8 particle radius scale

0.8 grid scale

0.5 separation rate

​

​

Test 18 -

​​​

lingering foam max lifespan = 5

​

0.05 velocity

​

foam group vorticity > 435

viscosity 0.08

​

birth threshold 0.5

death threshold 1.3

​

3 substeps

0.002 particle separation

0.8 particle radius scale

0.8 grid scale

0.5 separation rate

​

​

Foam group visualized

Vorticity visualized

With a base simulation to work with now, running the full 240 frames, I could continue working on the bubbles and foam volume. Previously, I created two layers of bubbles; one set of larger bubbles, then a second layer of small scale bubbles. However, I needed to create a third layer of bubbles that are based on age, allowing for newly poured liquid to create bubbles as well.

​

To create this third layer of bubbles, I used a color node to visualize the age of the particles. Then, within an attribute wrangle, I utilize this color value to extract younger particles.

Bubble layer 1

Bubble layer 2

Bubble layer 3

Still on the list to do is set up proper shaders and lighting, which I will be working on in the next few days.

FLIP - Iteration #3 - 05/18/25

During this weekend's work sessions, I wanted to begin preparing the simulations for rendering. For time sake, my previous simulations were within the frame range 1-120. This week, I ran simulations from frames 1-192, then began assigning very simple shaders to begin the lookdev process.

 

I noticed that when running the simulation for longer, there was a gradual volume loss. I ran a few tests with different birth/death rates, as well as particle scale. In this iteration, this issue of volume loss is unresolved, however I thought the tests help visualize what the parameter changes affect in the simulation.

Next work sessions, I am going to try to adjust the foam group's lifespan to reset to 100 after the fluid is done pouring. This may help keep the volume more stable. 

​

Test 8 -

​

foam group vorticity > 390

viscosity 0.08

​

birth threshold 0.75

death threshold 1.5

​

3 substeps

0.002 particle separation

0.5 separation rate

0.8 grid scale

0.95 particle radius scale

​

​

Test 9 -

​

foam group vorticity > 390

viscosity 0.08

​

birth threshold 0.5

death threshold 1.5

​

3 substeps

0.002 particle separation

0.5 separation rate

0.8 grid scale

0.95 particle radius scale

​

​

Test 10 -

​

foam group vorticity > 390

viscosity 0.08

​

birth threshold 0.4

death threshold 1.35

​

3 substeps

0.002 particle separation

0.5 separation rate

0.8 grid scale

0.95 particle radius scale

​

​

Test 11 -

​

foam group vorticity > 450

viscosity 0.08

​

birth threshold 0.5

death threshold 1.5

​

3 substeps

0.002 particle separation

0.5 separation rate

0.8 grid scale

0.95 particle radius scale

​

​

Test 12 -

​

foam group vorticity > 400

viscosity 0.08

​

birth threshold 0.4

death threshold 1.4​

​

3 substeps

0.002 particle separation

0.65 separation rate

0.8 grid scale

0.95 particle radius scale

​

​

Test 13 -

​

foam group vorticity > 400

viscosity 0.08

​

birth threshold 0.4

death threshold 1.4​

​

3 substeps

0.002 particle separation

1.0 separation rate

0.8 grid scale

0.95 particle radius scale

​

​

While I figure out the volume loss issue, I want to continue moving and chipping away at other aspects of the project. I began setting up a foam volume as the final element for the render. Using the original foam group, I create a new density attribute that is rasterized to voxels. When visualized, we can isolate the parts most dense with foam particles. Then, using an attribute VOP controlled via a ramp, we can isolate only the densest foam rising to the top of the fluid.

​

Now that I have a foam volume, fluid with bubbles, and secondary bubbles, I could begin bringing these elements into Karma to render. While the shaders are currently bare-minimum, it is enough to see any rendering artifacts or issues. For instance, immediately noticeable was the 'lumpy' quality of the fluid. While edits could have been made to the collision object, it was simpler to boolean the collision from the fluid until fully smooth. 

When rendering layered transmissive objects, I learned from the Harbor Collaboration project about dielectric priority. Following the same approach, as well as Arnold's documentation, I set the priority values accordingly: Glass to 3, Secondary bubbles to 2, Fluid to 1.

Arnold Documentation

FLIP - Iteration #2 - 05/13/25

Test 5 -​ spiral shape velocity field advected
 

2 velocity scale

​

foam group vorticity > 500

viscosity 0.09

​

3 substeps

0.0019 particle separation

0.45 separation rate

0.575 grid scale

0.75 particle radius scale

​

​

Test 6 -

​​

0.5 velocity scale

​

foam group vorticity > 425

viscosity 0.09

​

3 substeps

0.0019 particle separation

0.4 separation rate

0.575 grid scale

0.75 particle radius scale

​

​

Test 7 -

​​

0.8 velocity scale

​

foam group vorticity > 700

viscosity 0.1

​

3 substeps

0.0019 particle separation

0.4 separation rate

0.575 grid scale

0.75 particle radius scale

​

​

Building off of the previous FLIP simulations, I wanted to begin setting up a custom velocity field to encourage particles to spin/swirl in a more interesting way when poured. The bubbles in the reference video spin in a circular motion, so I wanted to test using a spiral to drive the velocity of the bubbles. The spinning also dissipates once the pour is complete, so If I use this velocity, I can keyframe the intensity of the force.

​

I start with a spiral, get a tangent attribute from PolyFrame, scatter points throughout the spiral, then spread the points out with PointJitter. This can retain the spiral motion while breaking up the uniformity. I then randomize the velocity and fit the range from -1 to 1. Last I rasterize the velocity attribute, which is called in a Volume Source in the FLIP simulation.

In order to add bubbles from the previously created foam group, I first isolate the group, then delete the desired percentage of the foam to use as bubbles. They should also vary in pscale and should be effected by lifespan, so I adjust these values with an Attribute VOP. For fun, I also converted the VOP network into VEX, as typically code is more intuitive for me if I need to make changes.

VEX conversion from VOPs

VOP setup from Nine Between tutorial

Now that I have the bubbles meshed, I can combine them with the original fluid. After converting the bubbles and original simulation to VDBs with a high resolution voxel size, I can convert the vdbs to polygons. 

FLIP - Iteration #1 - 05/11/25

Inside of a DOP network, I first input the Flip Object node to the Flip Solver. In this node, we can control some of the main parameters that can deal with volume loss/gain, simulation resolution and particle separation. Here, there is also one of three check boxes for adding a viscosity attribute.

​

Next, I plug the Volume Source node into the 4th input. In this node, I call the SOP path for the source points created previously, then check the "Source Particles" box.

The last input I am using for the Flip Solver in this first iteration is the Particle Velocity input. Here, I can use POP forces to help control the fluid simulation. Because FLIP is both Eulerian and Lagrangian, the simulation takes into account both voxels and particles. This allows for some unique controls, in which we can control the simulation using both volume and particle forces.

​​

In a POP Group, inside the VEXpression, I use an if-statement to create a foam group based on how turbulent the particles are. Because bubbles act differently than water, being less dense and hold their shape longer, I set the foam group to a lower density and higher viscosity.

Before the simulation, there are some final parameters / check boxes necessary to address. First are the birth / death thresholds, which tackle the issue of volume loss/gain. By increasing the birth threshold, fewer particles will spawn to account for decreasing density. By decreasing the death threshold, in the more cramped/dense areas, less particles will be killed.

​

There is also the separation rate / scale parameters, which can force particles away from each other when simulated. This does not work as well for changing the accumulated volume, but can impact the particle interactions themselves.

​

There are also a couple boxes to check, including adding ID, Age and Reap Particles.

Here was the progression of tests:

Test 1-

​​

first sim

​

2 substeps

0.0025 particle separation

0.5 separation rate

1 grid scale

1 radius scale

​

no other changes

​

Test 2-

​

adjusted source amount​

added vorticity and foam group

 

foam group -

vorticity > 400

viscosity 0.005

​

birth threshold 0.5

death threshold 1.5

​

3 substeps

0.0025 particle separation

0.35 separation rate

0.55 grid scale

1.2 radius scale

​

​

Test 3-

​​​​

birth threshold 0.75

death threshold 1.5

​

foam group -

vorticity  > 500

viscosity 0.1​

​

birth threshold 0.5

death threshold 1.5

​

3 substeps

0.0025 particle separation

0.45 separation rate

0.575 grid scale

1 radius scale

​

​

Test 4-

​

added drag and speed limit

added foam lifespan

​

foam group -

vorticity  > 450

viscosity 0.08

​

birth threshold 0.75

death threshold 1.5

​

3 substeps

0.0025 particle separation

0.45 separation rate

0.575 grid scale

0.7 radius scale

​

​

Setup - Collision and FLIP Source - 05/09/25

The first thing I needed to do was set up the collisions, which in this case is the champagne glass. In SOPS with some basic procedural modeling, I created a simple champagne glass driven by a line, deformed to a champagne glass profile, then revolved. Before the volume collision output, I use a ConvertVDB to convert the polygons to a volume.

​

In the FLIP solver, I will use both the surface and the volume as possible collisions. Sometimes particles can fall between the mesh using a volume collision, so using both is a common workflow.

Next, I create a DOP network and add two Static Object nodes- one for the surface collision, the other for the volume. We can use a Merge node to apply both surface and volume collisions to the simulation. The two should basically overlap, shown with the clipping blue/green collision guides below. **in the future I add thickness to the collision to avoid particles escaping**

After setting up collisions, I set up a source using a basic sphere. Adding a Flip Source node, I set an initial velocity directing the points into the side of the glass. I also apply noise to create more variation in the stream. Using Attribute wrangles, the last thing I set for the source points is viscosity and density. At the source, the viscosity stays close to 0 and density to 1000 to act as water.

Project Overview

Approaching the final project for SCAD's particle & procedural effects class, I wanted to begin exploring Houdini's FLIP with a small scale champagne pour. Breaking the project down into several parts, there will likely be multiple layers of simulation needed. This includes the main fluid pour, bubbles, foam, and the subtle fizz/mist that comes from the foam popping.

​

Here I will list the tutorials and references I will be using throughout the process:

​

Nine Between - Introduction to FLIP

Nine Between - Carbonation

Bubblepins  - Whitewater

Mesrop Hovannisyan - Fluid Splash

​

bottom of page