Particle tutorial trail spawner

From PopcornFX
Jump to navigation Jump to search
For PK-Fx Editor version : 1.5.1 and above
Main page: Particle tutorials

In this tutorial, we'll see:

  • How to setup a basic trail system.
  • How to use the spawner.LifeRatio variable in-script to change the trail across its lifetime.
  • How to deeply change the trail's apperarance.
  • How to use the Flipbook evolver.
trails in blast effect
Final result

Spawner evolver

We'll start with a new empty effect. Here we've set a backdrop, by selecting "Editor Properties > Backdrop Editor Backdrop.png" in the treeview, and selecting the "Models/CubeRoom_Default.hml" backdrop:

Tuto Trails 01.jpg

Our basic setup for the trails will be a simple effect that shoots a particle affected by gravity.

Here, the rig consists of a single infinite layer with a spawn rate around 0.5 particles per second.

We added a simple shape sampler 'sphere' named "SamplerVelocity" with a radius of 0.2, at position '0;3;2.7'
It is used in the spawn script to initialize the particle's velocity so that the particles shoot towards the shape:

Tuto Trails 03.png

The physics evolver has its 'ConstantAcceleration' set to '0; -9; 0'
This is what it should look like:

Tuto Trails 02.jpg

Now, we are going to create a trail. In Popcorn, trails are made by telling a particle to spawn other particles in its path. Our simple particles that shoot out towards our shape and fall to the ground will be the trail source that will spawn the trail's particles.

To create a trail, we'll need to add a new evolver: the CParticleEvolver Spawner.png CParticleEvolver_Spawner :

Tuto Trails 04b.png

The new evolver added in the treeview contains all the elements of a standard particle layer, except it is an evolver.
You should instantly see a trail of particles forming behing each source particle:

Tuto Trails 05.jpg

There are various parameters that control the forming of the trail. The main ones are the 'SpawnMetric' and 'SpawnInterval' values.
They control the distance between each particle of the trail. The metric used can be 'Distance', 'Time', or 'Custom'. (For more details on the 'Custom' metric, see CParticleEvolver Spawner.png CParticleEvolver_Spawner)

Most of the trails use mainly the 'Distance' metric:

Tuto Trails 06a.png

The SpawnInterval value basically says "the interval between each particle should be SpawnInterval units in SpawnMetric 's units."

For example, if the metric is 'Distance', setting a value of 0.02 in SpawnInterval will tell the system to spawn a trail particle as soon as the source particle has travelled more than 0.02 world units since the previous spawn.
If the metric is 'Time', setting a value of 0.175 in SpawnInterval will tell the system to spawn a trail particle as soon as 0.175 seconds have elapsed since the previous spawn.

Tuto Trails FrameBreakdown 01.png

Now, the current trails are too far apart. we'll bring the SpawnInterval value from 0.1 down to 0.01

Tuto Trails 06.png

The trails should now appear as a solid lines.

Tuto Trails 06b.jpg

Changing global trail aspect

Now that we've got basic trails set-up, we're going to see how we can change the global trail aspect, and create more interesting effects.

We will first reduce the base layer's spawn rate down to 0.2 particles per second, and increase the trail's particle's life from 1.0 to 3.0 seconds by changing the trail particles spawn-script:

Tuto Trails 07.jpg

Within the trail particle spawn-script, as with standard layer spawn-scripts, we have access to the 'float' variable 'spawner.LifeRatio', that contains a value between 0.0 and 1.0 representing the life ratio of what spawned the particle.

In a standard layer, the 'spawner.LifeRatio' will go from 0 to 1 across the emitter's lifetime. But in case of a trail, its value is the LifeRatio of the parent particle.

Therefore, we can use that value to detect if the particle is spawned at the beginning or at the end of the trail.

For example, to make a trail that starts big and end small, we can set the 'Size' property of the trail particles to a value that depends on 'spawner.LifeRatio' :

Tuto Trails 08a.png

This gives us a trail that starts big and ends small.
The particle size at the beginning of the trail will be: 0.4 * (1 - 0.0) = 0.4
And at the end: 0.4 * (1 - 1.0) = 0.0

It will vary linearly across the trail's lifetime:

Tuto Trails 08.png

Using flux functions

Initially, the particle spacing is constant (the SpawnInterval value in the trail evolver is a constant).
However, if, like above, we change the particle radius across the trail, with a constant interval, large particles will appear clumped together, and smaller ones will appear far apart, creating holes in the trail:

Tuto Trails 09.jpg

To correct this, we can either spawn a lot of particles, or find a way to tell the trail spawner to change its reference interval across the trail's lifetime.

Flux functions can be used to do just that: control particle spacing across the trail's lifetime.
To create a new flux function, right-click on the evolver trail, and select 'New Flux Function':

Tuto Trails 10.png

The editor will create a new flux curve, initially set to 0.0
As this curve represents the particle flux (equivalent to the particle rate, that is, the speed at which particles are emitted), a value of zero means no particles will be emitted...

Therefore, you will first see the trail disappear when creating a new flux function.

Just select the FluxFunction node and move the curve control-points to a value other than zero, and you will see the trail reappearing:

Tuto Trails 11.jpg

As you will have noticed, the higher the curve, the closer the particles.

We can now tweak the flux curve to make our particles touch each other across the trail, despite them having different sizes:

Tuto Trails 12.jpg

As a side note, in the above examples, the initial "solid" trail had around 1080 particles.

Using a flux curve, for a similar aspect, the particle count was brought down to 110 particles... that's ten times less !

Of course, those values will depend on your trails. Using a flux curve to get uniform spacing for a constant-size trail will be totally useless, as, by definition, the curve will be an horizontal line (a constant value). And it's faster not to use flux curves, so you should just tweak the SpawnInterval values instead.

Also note that flux curves do NOT replace the SpawnInterval value. They are combined with it, so you can change both to tweak your effect.

Using trigonometric functions to create spirals

We can also use the 'spawner.LifeRatio' to compute an angle, and use that angle to compute a rotation to create a spiral along the trail.

Tuto Trails 14b.png

Make sure you also check the 'UseVelocityOrientedSpawnMatrix' checkbox in the CParticleEvolver Spawner.png CParticleEvolver_Spawner's properties to make the spiral's axes follow the direction of the trail:

Tuto Trails 13b.png

This will make the trail particles spawn along a spiral around the source particle's path.

Tuto Trails 13.jpg

Tuto Trails 14.jpg

You can play around the constant values in the script to experiment with different spirals. (the value '10' above is the number of half-turns the trail will make, and the value '0.5' is the spiral radius)

Using a curve to control spiral radius

We can get a smoother and more interesting effect if we create a curve sampler, sample it and use it as the trail radius in the script:

Tuto Trails 15a.png

Tuto Trails 15.jpg

Tweaking the curve makes it look like a cheap rendition of infogrames's logo :D
188px-Infogrames logo.png

Tuto Trails 16.jpg

Of course, increasing the radius at some places in the curve makes the particles move further apart, and requires some tweaking of the spawn flux curve to keep the trail's solid look:

Tuto Trails 17.jpg

Here is the effect after a few changes in the layer's spawn rate and velocity-shape:

Tuto Trails 18.jpg

Tuto Trails 19.jpg

Smoke trails

We're now going to transform our abstract spiral-trail into a more believable smoke-trail.

First, start by setting up the trail's billboard renderer to use the "Textures/" texture, and its matching "Textures/BlastPackExtended.txt" atlas definition file.

Also, set the material to "AlphaBlend_Additive_Soft"

Tuto Trails 20.jpg

As soon as the atlas definition file is set, the billboard renderer will automatically add the 'TextureID' field to the list of particle fields (see the basic tutorials for more details on texture atlases).

Next, initialize the 'TextureID' field to the smoke sprites (texture ID 32, 33, 34, 35):

TextureID = rand(32, 36);

Tuto Trails 21.jpg

The trail looks a bit more like smoke now.
However, as you'll have noticed, the parent particle is still drawn as an ugly yellow blob.

We won't need to draw it. So just right-click on the parent particle's billboard renderer, and select 'Delete' to remove it.

Tuto Trails 22.png

Now, you will notice the entire trail disappears. Don't worry.

The 'Size' particle field is automatically added by the billboard renderer. Therefore, unless you manually declared it in the particle fields, removing the billboard renderer will remove the 'Size' property from the particles.

And the spawn script tries to modify 'Size' by affecting a value of 0.25 by default. Therefore, the spawn script produces a compile error:

Tuto Trails 24.png

Correct it by removing the line where 'Size' is used:

Tuto Trails 25.png

Hit Ctrl+S, and the trail will reappear, this time without the source particles drawn as ugly yellow dots.

Now, we'll add a curve sampler named "ParticleRadius" to change the particle radius, and replace in the spawn script:

Size = 0.4 * (1 - spawner.LifeRatio);


Size = ParticleRadius.sample(spawner.LifeRatio);

Tuto Trails 26.png

Tuto Trails 26b.png

We'll then add a rotation evolver to the trail particles, to make the trail less repetitive.

In the spawn script add the following line:

Rotation = rand(-pi,pi)

To randomize the initial orientation of the particles.

Tuto Trails 27b.png

Tuto Trails 27.png

This is what it should look like, after some tweaks to the flux function to accomodate the new sizes :

Tuto Trails 28.jpg

Making the trail expand with Velocity

Currently, once spawned, the trail particles stay completely static. To add motion to the trail, we're going to use the particle's velocities to make them shoot out of the curve's center.

As we already have the position in a spiral, we'll just set the velocity vector to the same vector, this way the particles will shoot out in the spiral's direction, and it will look like the spiral is expanding:

Tuto Trails 28a.png

Note the trick "Velocity = Position" works only because in the spawn script, all the positions and vectors (including velocity) are in the particle's local-space:

Tuto Trails 28b.jpg

To help you debug your scripts, you can visualize the velocity vectors of each particle directly in the viewport.
In the treeview, select "Editor Properties > Global Properties Editor Settings.png", and in the node properties panel, scroll down until you find "Float3StreamsToDebug", "DebugVelocityScale", and "ShowDebug"

  • Check the "ShowDebug" box (or check the Bug.png ShowDebug button in the viewport top toolbar)
  • Make sure "Velocity" is in the list of the "Float3StreamsToDebug" property
  • set "DebugVelocityScale" to a value around 2.0 or 3.0 to better see the vectors.

Tuto Trails 29b.png

The velocity vectors should appear in the viewport.
Note that you can use this visualization to debug any particle field of type float, float2, float3 or float4. Just add their name to the "Float3StreamsToDebug" property, separated by semicolons ';', and change the scale if necessary to better see the vectors in the viewport.

Tuto Trails 29.jpg

We can now add some friction in the physics evolver to finalize the movement of the trail.

Tuto Trails 30.jpg

Faking lighting of the smoke

As it is, the smoke trail is pretty dull, we will animate its size and color.

We will light-up the beginning of the trail as is it was spawned by some burning debris that light-up the smoke.

First, create a bunch of new particle fields:

float4 Color
float  ColorCoeff
float  SizeCoeff

Tuto Trails 31a.png

And a bunch of evolvers:

  • Evolver spline float4 "Color"
  • Evolver spline float "Size"
  • Evolver script (to apply the color and size coefficients to the curves)

Tuto Trails 31b.png

Size curve:

Tuto Trails 31c.png

Color curve:

Tuto Trails 31d.png

In the script evolver you just created, add the following lines:

Color *= ColorCoeff;
Size *= SizeCoeff;

Without these, the Color and Size curves would overwrite the values we set in the spawn script.

also change the spawn script and replace

Size = ParticleRadius.sample(spawner.LifeRatio);


SizeCoeff = ParticleRadius.sample(spawner.LifeRatio);

and add:

ColorCoeff = 1 - spawner.LifeRatio;

That last line will make the ColorCoeff go from 1.0 to 0.0 from the trail's start to end.
It should produce something like:

Tuto Trails 31e.jpg

Multiple trail evolvers

We're now going to add an animated fire trail to represent the burning debris.
This will take the form of a second CParticleEvolver Spawner.png CParticleEvolver_Spawner.

!! IMPORTANT !! There is a common pitfall you must be aware of when adding multiple trail evolvers:
Failing to specify a different partial interval field.

Note that this is not an issue anymore in Popcorn-SDK v1.5.3 and above, the runtime now automatically patches the particle declaration with the necessary fields if they are not specified manually. you can skip this whole section directly down to Burning source & flipbook evolver.

More details coming. First, let's just try to add a second trail, to see in practise what happens. Also change the spawn properties of the layer to make a fountain of trails to get a better view:

Tuto Trails 32a.png

Adding the second evolver immediately produces a second trail, using the default rendering. It looks like it's almost working:

Tuto Trails 32b.jpg

But if we look closer, we can spot problems. The trails aren't regular. they seem to be randomly broken:

Tuto Trails 32c.png

If we look at the editor's log (in the content browser window), we'll see a warning (here, l.330):

Tuto Trails 32d.png

The 'PartialSpawnInterval' field is a hidden field created by the CParticleEvolver Spawner.png CParticleEvolver_Spawner for its internal workings.
If you go and have a look at the list of particle fields, you can see that field at the bottom of the list:

Tuto Trails 32e.png

Here, the color-coding of the fields tell us this field (as well as 'PrevPosition', also needed by the evolver spawner), is a hidden field. This means, we can't access it inside scripts.
This field is where the evolver keeps track of the movement fraction left at the end of each frame. It is what ensures that trails are rock-solid and regular across frames, no matter what the framerate is.
During a frame, a trail can spawn multiple particles, based on the frame time, and on the spawn interval:

Tuto Trails FrameBreakdown 01.png

If the trail spawner didn't keep track of what fraction of the spawn interval was left at the end of each frame, it would produce an incorrect trail,
with particles always spawned at the same point at the beginning of the frame:

Tuto Trails FrameBreakdown 02.png

In order to avoid that, the evolver uses the 'PartialSpawnInterval' field to keep track of the interval's fraction left at the end of each frame, to be able to perform correct spawn computations in the next frame:

Tuto Trails FrameBreakdown 03.png

Therefore, when two trail evolvers use the same partial-interval field, The following happens:

  • Frame 1
    • Trail-1 reads 'PartialSpawnInterval', spawns particles. So far so good. spawns are correct
    • Trail-1 stores its final interval fraction for the next frame back in 'PartialSpawnInterval'
    • 'PartialSpawnInterval' now contains Trail-1's fraction
    • Trail-2 reads 'PartialSpawnInterval', and instead of getting its own fraction from the previous frame, it gets Trail-1's fraction. This errorneously shifts the spawns.
    • Trail-2 stores its final interval fraction back in 'PartialSpawnInterval', overwriting Trail-1's fraction.
    • 'PartialSpawnInterval' now contains Trail-2's fraction
  • Frame 2
    • Trail-1 reads 'PartialSpawnInterval', it gets Trail-2's fraction from the previous frame, instead of its own, spawns particles. incorrectly...
    • etc...

Obviously, the same goes on with 2, 3, 4, and more trails.
The solution when using multiple trail evolvers in the same layer is to specify a different spawn interval field for the 2nd, 3rd, etc.. spawners:

Tuto Trails 32f.png

When checking back in the particle fields, you can see the second spawner has correctly created the new spawn interval field

Tuto Trails 33a.png

When looking back at the trails, there should be no more interval errors:

Tuto Trails 33b.png

NOTE : This only happens with trails in the SAME EVOLVE STATE ! There is no problem whatsoever in an effect that has multiple layers with a trail in each layer, as the particles are different, and each trail gets its own spawn interval inside each particle.

Burning source & flipbook evolver

Now that we've got our second trail working, time to make it look like fire !
Set-up the billboard renderer like for the smoke.

IMPORTANT : Selecting the same properties, material and textures will ensure both trails will be rendered in the same batch, meaning the particles will have correct sorting order. Otherwise, the renderer is forced to use two batches and make two draw-calls, as the particles would have different materials. And this does not produce correct sorting.

Tuto Trails 34.jpg

We're going to use the animated flame texture in the "" atlas.

We could animate the sub-sprites by manually changing the 'TextureID' field in-script, like in previous tutorials.
But we'll rather use the more simple FlipBook evolver: CParticleEvolver FlipBook.png CParticleEvolver_FlipBook.

Tuto Trails 35.png

The flipbook evolver expects the first and last texture IDs.

You can get those by selecting the billboard renderer, and hovering your mouse over the rects in the atlas viewer. The TextureID is shown in the top-left corner:

Tuto Trails 35b.png

Tuto Trails 35c.png

You should now see the sprites animating along the trail, giving the false impression of a non-animated, fading trail:

Tuto Trails 35d.png

To improve the visuals of the trail, add a rotation evolver and tweak the Rotation, Life, and Velocity values:

Tuto Trails 36b.png

Note that doing:

Velocity = rand(0, 0.5)._0x0;

is equivalent to:

Velocity = float3(0, rand(0, 0.5), 0);

You could also write (although a bit slower to compute at runtime):

Velocity = rand(float3(0), float3(0, 0.5, 0));

Tuto Trails 36.jpg

Final tweaks

Finally, add Color and Size curves to further tweak the flames, as well as an evolve script to apply a color coeff:

Tuto Trails 37a.png

Tuto Trails 37.png

The final spawn script of the flames looks like:

Tuto Trails 37b.png

IMPORTANT : in the above example, extreme color intensities are used. With default editor settings, they will produce HDR glow in the viewport. However, note that if your final game does not have that kind of post-effect, the FX you create can have a very different look, and you'll need to use textures with baked-in glow.

To see what it looks like, you can selectively disable PK-Editor's post-effects by clicking on the post-effect button in the viewport's toolbar:

Tuto Trails 38.jpg

Screenshot of the final effect:

Tuto Trails 40.png

Previous tutorial : Audio spectrum sampler
Next tutorial : Advanced mesh & texture sampling