A Game of Tricks III – Particles fun (part1)

One of the first tool we developed for Drifting Lands was a brand new FX / particle system. The artistic direction of the game is headed toward a very graphic and stylized rendering and we do love traditional animation ! Here’s a sample of particle FXs in the current alpha version. Each of these effects requires from 3 to 20 or 30 particles max.

For Transcripted, we had used our Flash-like MovieClip system but it had a major flaw I didn’t want in the way this time : to have ‘long’ and smoothly animated FX we had to create a big array of bitmap frames and store them in atlases or spritesheets. Though we had total control over the animation, it was requiring a large amount of RAM and VRAM to store the image sequence if the FX was a bit too large.

a spritesheet for one of Transcripted FX

See this modest organic explosion effect above ? We’ll it’s kind of the only one used throughout all of Transcripted because it’s already taking way too much texture space for what it’s worth ;)

For Drifting Lands, we sure mean to have mighthy big explosions and all kind of pyrotechnic effects all over the place. Going the Transcripted way didn’t feel right from the start.The art style we have chosen will not suffer low-resolution or blurry textures stretched to big explosions. A few months ago, a game I’ve played helped me realize that there was other techniques to achieve extremely cool effects : Prince of Persia (the 2008 reboot by the talented folks at Ubisoft Montreal).

PoP2008

First, let me clarify something : I don’t mean in any way that this game was the first to use this technique but it is the first I’ve played that struck me as using it so apparently and so cleverly. In fact, I don’t mean either that this game is actually working as I will describe but it’s what I’ve interpreted and how we translated it in our own tools.

What is special about the FXs and particles in Prince of Persia? Well, they use a kind of threshold-based animation when they disappear (which is pretty standard) but a lot of them also have a different threshold-based animation when they appear. The fact that the appearance animation is different is quite important : it does not look at all like a ping-pong animation.

The simplest way to animate particles’ appearance and disappearance is through a simple fade of opacity applied uniformly. Often there’s not even any fade-in animation : the particle just pops and then fades away while moving, scaling, rotating, shifting color, etc… To produce visually interesting effects with those, you often need a lot of particles.

When I speak of threshold-based animation, I mean a kind of animated mask based on a gradient map. A threshold operation is the simple act of testing all the pixels of a bitmap against well, a threshold value. Pixels with a higher value are treated one way and pixels with a lower value are treated differently. Typically if you want to make a threshold animated mask, you use a grayscale map against an animated value : pixels ‘brighter’ than the current value are opaque, pixels darker than the current value are just ignored. It will give you something a bit like that :

mask_threshold

Notice how the edges of the disappearing shape are very pixelated and absolutely not anti-aliased. That’s your typical basic thresold effect revealing its binary nature. If you are familiar with Photoshop you may know, that you can achieve something very similar to a thresold mask on a grayscale base but with a much nicer quality : to do that, you have to use the “levels” controls. And you will end up with something a bit like that :

mask_AA

Better eh ? So what are we exactly doing by modifying the levels of a picture ? It’s really not that complicated : your base grayscale image is composed of pixels with values between 0 and 255 (you can only do that much with 8 bits). With levels, you kind of ‘resample’ the scale of brightness : you define a new black and a new white among the greys of your original picture. Everything previously darker than your new black is black and everything previously brighter than your new white is… white of course. Everything in between the values of your new black and new white is ‘stretched’ to cover the whole length of the luminosity range.

If you select a range with your new black and white which is very very very small. You’ll end up with something terribly similar to a simple thresold. If you select a relatively wide range of values, the effect will be more diffuse. But if you select a small range, you’ll get the previously presented result : something looking like a sharp shape with anti-aliasing.

mask_range

That last setting is perfect to do what we aim for in drifting lands : a nice animated mask with clear edges but not so much as to be devoid of any anti-aliasing.

We know what we want but how do we achieve that in Unity ? Well, that’s just shader work from there. Applying a level operation on a grayscale picture is just one substraction , one multiplication and a clamping of the result between 0 and 1 :

new_luminosity = saturate ( ( old_luminosity – B ) * S ) ;

B is the value of the new black between 0 and 1 in the range of luminosity of the original image.
S is the scale applied to the range of luminosity. S is actually equal to :
1 / range of luminosity from the original image if the range is expressed with a float between 0 and 1…
… or 255 / range of luminosity from the original image if the range is expressed with an integer between 1 and 255.
saturate() is a Cg function which will essentially clamped the parameter value between 0 and 1 (everything above 1 will be 1, everything under 0 will be 0, everything else doesn’t change).

As I told you earlier, what’s cool about PoP’s particles is that they appear and disappear with a different animation. So we don’t need one but two animated masks based on a threshold or level operation ! We need only grayscale informations and because it’s always a good idea to work with the minimum number of textures, we will fuse these two sets of informations into only one texture : one mask will be stored in the R channel, one mask will be stored in the G channel. Since we don’t like to waste, we will also use the B channel to modify ‘per pixel’ the size of the range applied in the level operations. By just applying a different value to the blue channel we can decide in the shader wether the appearance or disappearance will be sharp-edged, soft or even blurry for each part of the FX.

All these informations stuffed into a single texture will look a bit like that :

fxMapInOut

It’s clearly not something you can draw directly from scratch, so how do we produce those ? As usual when it comes to animation, we try as hard as possible to make it with Flash. Because we love Flash <troll mode>and no we don’t like HTML5 very much for now</troll mode>. Ok so we need one appearance animation, one disappearance animation (and usually we will add the optionnal mask in the blue channel by hand in photoshop). One thing you have to remember is that we can’t do EVERY animations with this technique because it’s ‘just’ threshold animation. So basically, for each frame of the appearance animation, you can only add stuff compared to the previous frame. And for the disappearance animation you can only erase stuff compared to the previous frame. Another thing is that for now, it’s only a mask. We will add colors later on but for now let’s consider only monochrome effects.

Our Flash workflow is as follow : we draw one MovieClip with the appearance animation. Usually we start with the end frame where the FX is complete (a flame, a cloud of smoke, a lighting bolt) and we move backwards erasing one bit every frame. Then we do another clip for the disappearance animation. Its first frame is the exact copy of the last frame of the appearance animation and we erase one bit every frame moving forward this time. In theory, we could have as much as 255 steps of animation but we usually only draw 10 to 20 or 30 steps for each half of the FX life. That’s already way more than what we could reasonably do with spritesheets in Transcripted ! For now, every steps of the animations are hard-edged but we will be able to choose the softness of the actual effect later.

 

Then it’s actionScript time : we just take the opacity channel of each frame of the animation, we divide it by the number of frames of the animation, and we draw additively each bitmap obtained on a black canvas. This will lead for the appearance animation to a black and white gradient image where the bright part is what appears first and the dark part is what appears last. For the disappearance animation, the bright part is what stays longer. We use an additional trick to save time : we add some blur to each step of our animation before the additive draw operation. Adding blur does not mean we will have a blurry FX in the end because this is decided only by the levels operation’s parameters (in the shader). Adding blur here is just a way to simulate additional steps to our animation. The result would be roughly the same if we had no blur and took the time to create 255 steps of animation. The 2 loops over the 2 MovieClips produce two greyscaled BitmapDatas which are then drawn into the red and green channel of the final image. We usually just press the print screen button, copy this into photoshop and possibly add some informations in the blue channel to define where the FX will have to be more blurry or more hard-edged when treated by the shader. The 2 animations do not have to have the same number of steps and will most likely not have the same duration in the game.

We’ll stop here for now… In the next part, we will study the actual implementation of everything we’ve seen so far in the Unity engine and how we can add some cool extra color manipulations to enhance the effects.

You can download here a zip file with a FLA (CS5 or higher) example of our export process. The actionscript code is included in the first frame of the timeline and the exported clip must be on the stage and named ‘clip’.

See you soon !

If you like what we do, please share our work by liking our facebook page by following us on twitter or sharing our youtube channel. We’ll self publish Drifting Lands and we need all the love we can get out there ;)

15 thoughts on “A Game of Tricks III – Particles fun (part1)

  1. Can you post an example flash document? I have no idea what I’m doing in flash, and seeing an example would help. I love this effect, would love to incorporate it into my game!

  2. Pingback: A Game of Tricks III – Particles fun (part1) | Les Liens d'Epholys

  3. Pingback: A Game of Tricks III – Particles fun (par...

  4. Pingback: Unity | Pearltrees

  5. Pingback: Gregory Smith

  6. This tenchnique looks really powerful and I am keen to implement it myself. I have a question though (although i know you hope to cover it in a later post), how on earth do you keep track of the previous luminosity. Especially on a per particle basis, I can’t fathom where or how to store it. And if I am misreading it, and old_luminosity is a constant (per pixel) and refers to the luminosity of the fixed mask you create, then how do you animate the B value on a per particle basis. I imagine that in unity animating a global property of B for the shader would result in synchronised animations among particles, and not particles cycling through their animations independantly. Sorry for the bother, I know you are very busy at them moment, but I do hope you can shed some light on this part. I can’t wait for more posts and am really excited for this game!

    • Hi, yes I’ll cover eventually this topic in the following part of this tutorial. It’s terribly long in the coming and I’m sorry. I have a lot already written but I can’t find the time and motivation right now to finish this. Knowing that people are actually interested in this may help :)

      But I can still answer your particular question. In the past I used to store per particle informations in the geometry data (Vertex color mainly) which can be actually helpful but is sub-optimal for particle effects. Modifying geometry is actually quite slow and even if it allows to render in a single batch a lot of particles because they share the same material it’s not the way to go. There’s a concept dedicated for doing just that in Unity and it’s called MaterialPropertyBlock. With MaterialPropertyBlock you can assign different values to individual objects and still use the same material for every item of the batch. So our custom particle system does store a list of every particles with a bunch of properties for each of them : current age, total duration, position, speed, etc. The particle manager will then update on each frame the gameObjects of each particle and pass all parameters necessary to the rendering of the material through a MaterialPropertyBlock. The shader will do the rest with this values…

      • Wow, thanks Alain, and so speedy too! That’s super helpful to know about, I wasn’t even aware of their existence. I guess I have just been stuffing all sorts of data into tangents and what not to get by, but these are really gonna help me create much better shader effects all over the place. I guess my only follow up question would be how you decide if it should be the [in] or [out] animation playing without conditionals (if/else statements) in your shader, or whether you do in fact use them. I’ve been trying to get into this mindset of no if/else in shaders because I’ve heard they really degrade performance. Otherwise, thanks for the help once again and looking forward to that blog post (and to see what you do in terms of that color manipulation)! :)

        • Ah yes very good question the in and out problem :)
          I’ll be very honnest here : I’ve heard like you that “if” instructions affect dramaticaly performances in shaders. Usually I don’t take any statements of this kind for granted until I’ve tested it myself. In this particular case, until now I haven’t tested the actual difference in performance. And that’s one of the reasons, I haven’t finished my tutorial. I have to find the time to make this test to be sure I don’t write anything stupid.

          But right now we don’t use ‘if’ statements. How do we do : Well imagine you’ve got two equations calculating independantly apparition and disparition based on the current age of the particle. What you want in the end is either one or the other. To achieve that you only have to discard one of the them by multiplying it with 0 a bit like this :

          Consider a particle where the apparition and disparition have a duration of exactly half the life of the particle

          fade_in_ratio = saturate (life/duration*2); // this will go from 0 to 1 from the start of the life of the particle to half of its duration (end of apparition)
          fade_out_ratio = saturate(life/duration-0.5f)*2 // this will go from 0 to 1 from the half of the life of the particle to the end of its life (end of disparition)

          final_result = apparition_formula * step(1, 1-fade_out_ratio) + disparition_formula * step(1, fade_in_ratio)

          step(1, 1-fade_out_ratio) ==> this term will keep a value of 1 from the start of the life of the particle and will instantly turn to 0 when reaching the start of disparition
          step(1, fade_in_ratio) ==> this term will keep a value of 0 from the start of the life of the particle and will instantly turn to 0 when reaching the end of apparition

          It may seem a bit overkill but that’s how we do it right now :D

          • Great, that’s really useful to see how you do it. I was considering doing it by setting a float property that was treated like a boolean by the c# script that could only be set to 0 or 1 (0 for in animation, 1 for out animation). Then I was going to switch the property value when needing to switch animation and calculate the old_luminosity using;

            old_luminosity = ((1-animationToggle) * mask.r) + (animationToggle * mask.g);

            if animationToggle is zero then only get mask.r, if animationToggle is 1 then only get mask.g. Then I was going to animate the B range differently in the C# script based on out animation. Never got round to coding it though, but will try both today and see which one is a better fit.

            Once again thanks for the advice and info, I imagine you have my email by now from all the posts so send me a message if you wanna have a chat about effects or anything like that, hopefully I can help you out sometime as thanks! :D

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>