This is a small breakdown of an stylized smoke effect made for SneezeFire a game made for the GMTK Jam 2020.
So lets start with the material.
In the jam I used Houdini to create meshs with required vertex color for the effect.
The red channel is primarily used to describe how heating should distribute over the mesh or in other words, how each cloud should cool. The red is a height gradient, and baked into the mesh - as it allows greater freedom and some squashing of the heat at the top.
The green channel is used to bake in some noise information. It is used to create color variation and the erosion (fading out) of the smoke effect. A small trick I got introduced by Simon Trümpler years back.
I then picked some of the meshes and put them into separate emitters. You could have made a bigger material which uses UV/Vertex blue information to random pick a sub-mesh from single one, but in this case I made it easy.
💡 Note: In Unreal Engine 5EA / 4.27 Preview it is now possible to use mesh arrays for variantions rather than multiple emitters. Which makes this picking logic “redundant”. There are also mesh flipbooks and other fun stuff worth checking out.
The material consists of 3 main parts.
At first we calculate the color by how “hot” the smoke still is. For this we create a blend between a yellowish tone and the green channel of the vertex color (aka noise). The red channel of Vertex Color is subtracted by a Dynamic Parameter (parameters coming from a niagara effect) to create some falloff - this is the actual “cooling”. Which is used as an Alpha to blend between “Hot” and Smokey-Color.
💡 Note: Alternatively to fixed colors it might be worth checking out the material node:
BlackBodythis converts temp to proper “heated” colors. But you would need to boost the “heating to cooling” after saturate by about 2800.
The outcome is then boosted by a fresnel effect, which gives it some oomph on the edges.
To build everything together we utilize some modules in Niagara. Let’s go over the basics
I used a second emitter with another mesh to create an inner finner line of the effect. So one does an outer layer and one a finer core. But both are basically the same just different scale and different sizes, and different spawn amount. So I’ll go over one of them and note if there are big differences or where to change those values.
At first we utilize Spawn per Unit to uniformly spawn them when we move the emitter in our game when being attached to an Actor and launch them up when they have their bottom burned.
For every particle we spawn we initialize it with a random lifetime (which we’ll utilize also in this step to scale). We slightly also randomize the orientation to give it a more smoke feel of the heat.
An important part to the feel is that we utilize a direct set to set the
Particles.Scale by the lifetime. The curve goes from 0..1 (same as max lifetime) and set the scale between 1..3 (meaning, minimum lifetime is around 2.0 scale)
As scratch pads didn’t exist back then or I didn’t know them, I would like to add here, use them for this quick sets at it is easier to edit or create a niagara module out of them later on.
The next we utilize a SphereLocation to basically set the radius of the smoke emitter, this is where both emitters differ one has a bigger radius (50) the other is a lot finer (10). Velocity from point basically gives the single smoke cloud a little drift sideways from the center of the emitter in world space. An Inherited Velocity gives the smoke a bit more updrift so when an actor moves up it seems like it follows.
So each cloud gets smaller we utilize a Scale Mesh Size to modify it over it’s lifetime which works very well with the Lifetime to Scale from Particle Spawn.
The heating and erosion curves are coming now. Similiar to size we also utilize the NormalizedAge to apply Cooling. Reducing the Heat, and therefore blending between Red/Yellowish to Greyish Smokey Colors. For the Erosion we simply push it into the negative over the particles lifetime to get the proper effect. Usually you want erosion to be in 0-1 range but I quickly throw the material together during the jam and just fixed the values how it would look good.
Additionally we use some curl noise, which is very small - you could either get rid of it completely (as it is not noticeable). But if you increase it you can get a more floaty and noisy smoke which may be something you want to go for.
Next we added some upward acceleration over time to each smoke puff. Write out the Dynamic Parameters from Particles Namespace so the material can utilize it. This seems to be redundant later as in UE5 there are already signs of writing directly to Material Parameters rather than going through Dynamic Material Parameters (but only for Sprite Renderers for now).
Also we apply increasing drag to all forces the more the particle is cooled. See the CurveIndex is set to
Particles.Cooling rather than
Particles.NormalizedAge like it is by default. This allows you to get really fancy and creative with your applications of curves.
Notice: The Scale Color is there from the base emitter, and was forcefully set to (1,1,1,1) from a Curve as we don’t need to scale color here (due to heating and so on) I used it for dust from the knights too and there it is used to make the dust lighter the higher up it goes.
In the renderer we just set the mesh to one I created in Houdini. In the end I have a single mesh for the outer “bigger ring” and one extra one for the inner core, which may stay a bit longer once things start drifting apart.
During polish we also added SSAO in the Post Processing Volume which enhances with the meshes creases and blobby look.