Firework(pyro)
research
FX
· Houdini
· Unreal
· Flame simulation
· Houdini - Unreal workflow
· Zibra VDB compression tool
· Houdini Pyro render
Week 0 - flame approach and Zibra test
This is a 4-week Pyro simulation project aimed at real-time rendering.
Before starting the project, I need to do some preparation — I need to understand how flame simulations work in Houdini and how to export them, as well as how to import VDB models into Unreal Engine 5 using Zibra. Once I’ve confirmed that this workflow is viable, I can move forward with the project.
1. pyro and flame simulation
I looked through some tutorials on fire effects and found that both fire and smoke simulations are based on Pyro. The methods for creating them are almost identical.
First, use Pyrosource to convert the object serving as the emission source into a point cloud. You could also use other methods to convert polygons into points, such as Points From Volume, but we recommend using Pyrosource directly. This is because it automatically creates the attributes you need.
The density attribute is clearly the key factor. Both smoke and fire use this attribute as their emission source. The temperature and burn properties control the shape of the flame.
Here’s a simple example illustrating how these three parameters control the flame by default:
density controls the density of the pyro and also affects how it is affected by gravity and force;
temperature controls the temperature within the area, where pyro activity is more intense in areas with higher temperature weights;
and burn controls the shape of the area emitting the flame.
We then use Volume Rasterize Attributes to convert the points into a VDB, which serves as the input for our solver. Note that the attributes we created earlier will be converted into VDB attributes as they pass through this node.
In this tab of Pyro Solver, you can see how to use attributes to control the target field. This allows you to use custom attributes to control the field.
And how to shape the flame. If you want to set up a specific wind field, you need to dive into the solver node and use the force output as the output node.
Before learning about fire, I thought that “fire” and “smoke” are two different things that need to be created separately. In fact, they are both types of pyro, and fire and smoke can be created together by the Pyro Solver. The Solver(sparse) is a solver specifically designed to generate smoke within a dynamic region, which means the solver checks and crops out areas without smoke in every frame. In general, the result of the calculation will take up less memory but it seems this might also slow down the processing speed.
*Update: Running Pyro simulations here in Dopnetwork isn’t a good idea. A better approach is to use DOPNetwork at obj level or to use PyroSolver for smoke simulations. Using Dopnet within Geo isn’t straightforward - it requires DOP Import Field to view the results. Furthermore, micro-solvers would be more suitable for complex situation.
2. Zibra Pre-test
Before begining, make sure all the necessary plugins have installed. I’ve included some resources that might be helpful here. Although Zibra is free, you still need to activate a license to use it. Please visit the official website for instructions.
· Sidefx - Zibra instruction page:
ZibraVDB for Houdini
· Zibra official website for Houdini plugin:
Download ZibraVDB for Unreal Engine | Real-Time VDB Compression Plugin
· Zibra - Unreal 5 instruction page:
https://zibra.notion.site/ZibraVDB-for-UE-1-5-2fdbc784cfb380e0b041ff9fea634d82
· Zibra Unreal 5 plugin FAB link:
Plugin for VDB Compression and Real-Time Rendering — ZibraVDB (Personal Tier) | Fab
· Zibra samples:
ZIbraVDB Sample Effects
Using Zibra isn't difficult. The plugin uses a special filecache to store VDB data. Compared to a standard filecache, using this one may avoid some unnecessary trouble.
The file extension for the filecache will be .zibravdb. You can use ROP to further compress the filecache. This step may slightly degrade the quality, but usually not by much.
So it seems that both Pyro types can be successfully imported into Unreal 5 via Zibra. The issue is that the fog density and color look different from how they appear in Houdini. However, these can all be adjusted within Unreal.
The official documentation outlines two different approaches: including pyrolook before filecache, or omitting it. The advantage of the former is that minimal adjustments are required after importing into Unreal; the advantage of the latter is that it offers greater flexibility for customization within Unreal.
final result
The color of flames and smoke can be adjusted directly in Unreal. The corresponding Houdini attributes for density, flame, and temperature are density, burn, and temperature.
Week 1 - Start
Topic choice: To create something suitable for the Zibra process, it’s best to look for reference with more smoke and fire. Therefore, fireworks would be a good choice in this case.
Reference: firework (click here to watch the video)
To know how to do it, it’s best to first understand how fireworks work:An animation for firework explosion theory
As you can see, there are explosion, fire, smoke, sparkle.
So the rough idea is to use -
1. RBD bullet for launching firework powder;
2. Pyro for smoke and fire;
3. POPnet(or Unreal Niagra particle) for sparkle.
4. Pyro burst for explosion
So the first step is modeling. there’s no difficult models in the scene but I have to guess the structure of the firework launcher. The video shows that each box contains four shells. After launching the shells, it emits a second round of small sparks(later do this as particle).
Step 02 - Explosion
Set a source for each emitter. I used a circle as the source because the flames appear cylinder-shaped. Use Enumerate to create an index for each instance. I named the index attribute explosionid. Then use this attribute to control the startframe — create a startframe attribute using Attribute Adjust Float, and set the max value as three times the source range (I have 28 instances here). This will result in a startframe that gradually increases in 3-second increments.
Quickly generate an exploding particle cloud using the Pyro Burst Source. Use Volume Rasterize Attributes to convert the particles into a volume (don't forget to enter the source attributes in the Attributes blanket).
Use the previously obtained emission source(the circle array) to trigger the explosion. explosion will be triggered every 3 frames in order. BurstSource actually outputs a static point cloud rather than a DOP result, but rapid scaling and fading are sufficient to create an explosion effect, so it can be exported directly without the need for DOP simulation.
The explosion will produce smoke. Unlike the smoke from fireworks, this smoke is denser and will settle to the ground and spread out. Therefore, use the point source from earlier to run the DOP simulation. In DOP, use Pyro Sparse to simulate the smoke.
If you're unsure how to configure the volume source to drive the target field using attributes, simply select “Initialize-Source Smoke” at the top of the volume source; the node will automatically set up the preset attributes and corresponding fields. Here, I add an option using “burn” to control the target field “divergence”, meaning that the higher the burn value, the more the smoke expands.
Step 03 - flame
After the explosion, the launcher immediately shoots out flame. If flame is generated using regular methods, it moves too slowly; using the explosion method described above doesn’t produce dynamic flame. So I came up with an idea — why not combine the two approaches? By using a pyro burst source to generate a sudden burst of particles, and then using those particle clouds as a pyro source to create flames, this produces flames that appear to erupt suddenly.
Unlike before, the burst type needs to be changed to “muzzle flash.” Also, various sizes need to be adjusted. Another very important point is adjusting the frame duration and expansion, which will determine the duration and shape of the flame.
At last use Pyro Bake Volume to give the fire a final look.
Step 04 - Launcher RBD
According to the reference video, each box should hold four shells, and they should be fired one after another rather than all at once.
Since my four shells are instances generated by Copy To Points, when I run an RBD simulation after entering DOP, these four instances will be launched simultaneously (because they belong to the same object). So the question is: how to trigger the launch in the correct order?More specifically, how to fire a shell every 0.25 seconds?
Approach 01 - Active attribute + DOP network
Use the “active” attribute to activate instances one by one. The idea is to assign an “id” attribute to each instance, and then every 0.25 seconds, change the “active” value from 0 to 1 for the instance with the corresponding “id”. Once activated, set v to (0, 8, 0).
Result: I can’t set up “active” attribute this way. DOP can read the “active”, but it does not seem to be applied correctly to every instance.
Approach 02 - RBD Key Active in DOP network(suggested by Chat GPT)
Chat GPT recommand me this method: use RBD Key Active in DOP to activate the emitter objects one by one.
Fill expression in Active Value and Activation blanket. That means objects are activated every 6 frames based on their names.
Result: I can’t run RBD Key Active correctly - As we know sometimes AI will give out wrong solution.
Approach 03 - Startframe attribute + DOP network
Use the Attribute Adjust Float to set individual startframe for each instances. The idea is to use “start frame” to control the activation time for each instance in DOP.
Result: I can’t set up “startframe” attribute this way.
Approach 04 - Activation + RBD bullet solver
Same method as the first approach, but give up DOP network and use RBD bullet solver instead. This node inherits attributes from upper stream, and in fact, “active” does work correctly this time, but this solver doesn’t have an initial speed feature.
So I created an additional velocity attribute “v”. However, “v” maintains the ball's velocity, preventing it from being affected by gravity. So I added a condition to make the velocity effective only on the first frame, but this caused the subsequent upward velocity to drop to zero.
Result: I can’t set up initial velocity this way.
Week 2 - Pyro
This week, the issues from last week have been resolved. Special thanks to Professor Deborah Fowler for helping me solve this problem.
Her solution is - use the SOP solver in DOP to set “active”, then the shells will be successfully launched in order. Later I set “v” in the same way, which will give initail velocity to each instance.
Conclusion: Use SOP solver for instanced objects.
Step 05 - Firework smoke
At first, I planned to use the shells directly as the pyro source, but their high speed caused the smoke to break up. So I used Pop net to emit particles and used those particles as the pyro source. This made the smoke much more continuous.
Observing the reference animation, note that the smoke at the top of the firework moves very slowly, while the smoke at the bottom swirls and expands rapidly. Therefore, set a range for the temperature so that the smoke at the bottom is higher. Here, I use a sine wave to make the transition smoother. At the same time, temperature is also used to control the velocity.
Use a “for each” loop to replicate the effect of launching every 0.1 seconds
Week 3 - Modification
I finished the firework smoke simulation this week, though the smoke may still need some adjustment.
To match the shape of the smoke in the reference, I refined my approach: I used the Pyro trail path to generate emission trails, then rasterized these trails to convert them into a VDB, and finally merged them with the VDB of the fireworks smoke.
The final result will look like this. Similarly, this method can be used to create small fragments and spark trail smoke.
To make the smoke less uniform and ensure it is influenced by wind and turbulence, I duplicated the source seven times before running the Pyro simulation. This allows me to obtain a complete set of smoke simulations. But at the same time, it’s very expensive. I also add a bit of random scale for each iteration so the smoke will not look too regular. My computer’s memory can’t handle such a large simulation, so it always crashes around 150 frames.
I tried using Pyro Bake Volume for shading(or, to be more accurate, coloring), but the results weren’t great.
First, even if I use Pyro Bake Volume for coloring in Houdini, the colors aren’t carried over when imported into Unreal, so I have to do the shading in Unreal anyway.
Second, Zibra doesn’t read custom attributes in Unreal, so I tried using the temperature property to create a mask in Houdini, but it didn’t work well.
The logic of coloring is different in Houdini and Unreal.
Step 06 - Zibra
I met one issue here: Zibra does not support compressing more than 8 channels. These 8 channels include point, primitive, detail, and VDB, so ensure that attributes are deleted until there are fewer than 8 channels remaining.
Zibra is no different from a standard filecache; the only thing to keep in mind is the compression ratio. Different sizes of smoke suit different compression ratios.
Step 07 - Pyro in Unreal 5
A single firework smoke filecache is 36 GB. After Zibra compression it is 1.6 GB. So when I put four smoke into scene Ureal is exhausted.
At the same time an issue arises - the smoke exhibits strange moving in the final few frames.
Week 4 - Rendering
Step 08 - Pyro final rendering in Houdini
My computer has limited memory—only 64 GB—so I can’t cache too much smoke. That’s why I’ve divided the smoke into four groups by color, simulated each group separately, and then duplicated them. This also makes it easier to shade.
Although the mask I created earlier using the “temperature” attribute didn’t end up being useful in Unreal, it can still serve well in Houdini. In the Material Library, create a Pyro material and use my favorite node, Mtlx Geometry Property Value, to import the attribute mask (in this case, temperature) to control the smoke color.
Final render
Because the project was too large, I had to split it into two segments for rendering. When exporting the second segment as a USD file, my memory ran out, causing Houdini to crash and bringing down my computer system as well. Keep an eye on the export status—this is something to watch out for in future work.
Apart from that, there is still plenty of room for improvement in the project as a whole. Many thanks to Professor Fowler for her suggestions - the smoke may require an adjustment to the disturbance value. Additionally, the variation in voxel size caused by the merging of several pyro sources may be one of the reasons for the excessive difference in smoke density between the upper and lower layers, which is worth noting.
- END -