Advanced Particle Expressions
This final exercise is going to be a bit of a leap. If you have followed along so far and are comfortable with the type of animation workflow we've used in the previous exercises, you should be up for the challenge. If you're already familiar with animating particles, this section might teach you a few new tricks to try out on future projects.
This tutorial will take you step-by-step through the process of creating a particular effect: an eerie spectral cloud with elements of a time-lapse look. We want a turbulent cloud to appear in the air, grow, and then taper off. And we want some controls so that we can edit the effect after it has been set up. These controls are simple locators that could be animated or parented to the limbs of a wraithlike character. We're going to look a little closer at the goal weight per-particle and goal world position per-particle attributes as well as some ways to use ramps with expressions.
First, we want a turbulent stream of particles. We'll control their size and visibility by creating relationship between their per-particle position and the position of a locator.
- Create an emitter, make it a volume emitter, and set its volume type to sphere. Name it cloudGen. Set the rate to 400.
- The emitter should appear at the origin. If you play the animation, a semi-dense cloud of particles will spew out from within the sphere. Select the particle shape and choose Fields ■ Uniform.
- The uniform force is just a force that pushes the particles in a particular direction. Really, the gravity field is a uniform force that pushes particles down in Y. Move the icon for the force a few negative units in X, just so that it's easier to see.
- The force should start pushing the particles in the positive X direction. If nothing happens, double-check the connection between the particle and the field in the Dynamic Relationship Editor.
- Let's calm down the motion a little. Set the conserve of the particle shape to 0.7.
- Now let's randomize the motion. Select the particle shape and choose Fields ■ Turbulence. Set Magnitude to 30, and set Attenuation to 0. Make sure Use Max Distance is set to Off.
- That looks cool, but the turbulence pattern itself is a little static. Let's get the noise pattern of the turbulence to animate a little. Select the turbulence field. In its Channel Box, right-click the PhaseZ attribute and choose Expressions.
- In the Expression Editor, type the following in the Expression field:
- phaseZ=sin(time);
This expression moves the phase of the turbulence back and forth in Z over time in a sine wave pattern. Play the animation. Looks neat. You can also try noise(time), which randomizes values from -1 to 1 in a connected manner that looks very natural.
- It's not a bad habit to immediately assign a per-particle lifespan attribute to the particle shape. Even if you end up with something plain like a per-particle lifespan of 1 second, at least you have the attribute there, set up, and ready to use as an input value in any subsequent expressions or ramp. As we did in previous exercises, set Lifespan Mode to lifespanPP Only. Scroll down to the PerParticle (Array) Attributes section, right-click the Expression field, and choose Creation Expression.
- For the creation expression type the following: particleShape1.lifespanPP=rand(5,7);
- Already this looks good enough for your demo reel. Okay, no it doesn't, but it does look rather cool. Switch the Particle Render Type setting to Cloud(s/w). For the time being, do not click the Add Attributes For Current Render Type button.
- Create a locator and move it about 12 units in X. This will be our size control, which will help us create a sort of time lapse, condensing water vapor effect.
We are going to set up a relationship between each particle's position and its proximity to the locator. From this, we will control the radius of the particle so that it appears and disappears as it passes the locator. For this, we'll use the goalWorld-PostionPP attribute. You cannot directly animate this attribute; it just tells you the world position of a goal as it relates to each particle, but this is a useful input value to drive expressions. To get at this value, we need to make the locator a goal and also create a per-particle goal weight attribute.
- Select the particle, Shift select the locator, and choose Particles ■ Goal.
- If you play the animation, you'll see the particles shoot at the locator depending on the goal weight set when the goal command was activated. We'll eliminate this in a second with the Per-Particle Goal Weight attribute. Open the Attribute Editor for the particle. In the Goal Weights And Objects section, click the Create goal-Weight0PP and the Create goalWorldPosition0PP buttons (see Figure 19.15).
- Figure 19.15 Attribute Editor buttons for each goal object allow you to add the new goal weight, position, normal, tangentU, and tangentV per-particle goal attributes.
- Let's start by making a creation expression for goalWeight0PP. To do this, move down to the array attributes, right-click the goalWeight0PP field, and choose Creation Expression. In the Expression Editor under the lifespanPP expression, enter the following:
- goalWeight0PP=0;
Click the Edit button.
- If you play the animation, you'll see that it looks just as it did before we added the goal. However, now we have access to the location of the goal through the goalWorldPosition attribute. The locator goal is acting as a way to get additional information about the scene in the expression.
- Go back up to the render type for the particle object and switch from Cloud(s/w) to numeric. Click Add Attributes For Current Render Type. You'll see each particle turn into a string of numbers. In the new attributes area, you'll see a field for attributeName. In this field, type goalWorldPositionOPP. The particles should look something like 12.000,0.000,0.000. By no strange coincidence is this the current location in X, Y, and Z of our locator goal object. If we move the locator and play the animation, these numbers will change accordingly (see Figure 19.16).
The numeric render type is a handy diagnostic tool. It doesn't render, but you can type the name of an attribute currently being used by the particle into the attribute name field and see the particle update accordingly. Try typing birthTime in this field, and you'll see the particles telling you when they were born in terms of seconds.
1. Switch back to Cloud(s/w) for the render type. Let's try working with the radius of our particle.
- Figure 19.16 When the particles are set to the numeric render type, you can access per-particle information directly in the camera view.
Figure 19.17 The Add Attribute window allows you to create new custom particle attributes or select from a list of attributes.
Figure 19.18 The turbulent cloud of particles pushed toward the locator
0 O 0 Add Attribute: | particle 11 partidelhape 1
Help
I Particit I
yrwlWflyFiiTPP
0(HlWeiBht3PP
qojIWciqhMPf1
incandescence
Incandescence«»
llneWidth mull ¡fount mutti Radius normal Dir parent«
parentu parentV
ptMFIlSi/l"
radiusO
radius 1
radius radrusW
selectedOnly sf>i i I i-M urn spriteNumPP
spritcScaleX
spntcScalcXF'P
Click the General button in the area just below the array attributes section. From the pop-up menu, click the Particle tab and choose radiusPP from the list. You'll see a new radiusPP attribute added to the particle's array attribute list (see Figure 19.17).
Make a creation expression, as you did for the lifes-panPP and goalWeightPP attributes. The expression should be particleShape1.radiusPP=0.1. If you play the animation, the particles are now smaller circles (see Figure 19.18).
We now have our creation expressions set up, and we've defined a starting point for our particles from which we can build some runtime expressions. With the Expression Editor open, select Runtime Before Dynamics Mode from the particle options. First, we need to create some variables. These variables will hold values; we'll manipulate these values and then set the attributes equal to these new, manipulated values. Variables give us flexibility and allow us to gain greater control over the attributes.
Figure 19.18 The turbulent cloud of particles pushed toward the locator
In each frame of a particle's life, the runtime expression will be executed, which is why expressions can be a little slow compared with other means of controlling attributes, such as Set Driven Keys, the Connection Editor, and utility nodes. However, they offer more direct, custom control. If expressions still can't get the job done, you can break out the MEL scripting (see Chapter 16). If that doesn't do it, you can use Maya 8.5's new Python script integration to create C++ style plug-ins for Maya. This new feature makes it a bit less scary to create powerful plug-ins that tie directly into Maya's API.
The first variable we'll create is one that will store the current per-particle radius value. We get this value each time the runtime expression is executed. Since the creation expression set this value at 0.1, for now we know this value will be 0.1. If the per-particle radius has changed by the end of the last frame, then at the start of the execution of this runtime expression, the variable will have that new value. Hopefully, this will become clearer in a moment. The syntax for creating a variable is as follows:
float $size=particleShape1.radiusPP;
In the first part of the variable syntax, we establish the type of number the variable will hold. In this case it is a float, which is a single positive or negative decimal number such as .1 or -3.14 or 0.33333333.
The second part of the variable syntax is the name of the variable. We chose $size since it is short and descriptive. Variable names must be preceded by a dollar sign ($).
We then set the variable equal to the value of the attribute we want the variable to contain. In this case, it is the radiusPP of our particle shape.
Finally, all statements must end with a semicolon in the Expression Editor. (If you forget, as everyone does now and then, Maya will let you know you've made a mistake by displaying an error message.)
Now let's create a variable to hold the location of the locator. We could make a variable just to hold the translate X, Y, and Z value of the locator, but instead we'll be using the new goalWorldPosition attribute we've set up. Why? There's a good reason that you'll learn in a little while. Type the following:
vector SgoalPos0=particleShape1.goalWorldPosition0PP;
A vector is another variable type. A vector is a set of three numbers such as translate X, Y, and Z values or red, green, and blue values. Vectors can be added, subtracted, multiplied, or divided by other vectors directly. However, you can't perform mathematical operations between vectors and floats directly.
Now let's create a variable that will hold each particle's position. This will also be a vector. Type the following:
vector Spos=particleShape1.position;
Hold on a second. Up until now, whenever we've encountered a per-particle attribute, we've seen the giggle-inducing PP attached. However, position has no PP. You can verify this when you look at the list of attributes at the top of the Expression Editor. Why no PP?
Well, position is a per-particle attribute. It just is—and there's no PP attached. Just one of those things you learn and remember when you work with Maya. It's a quirk of the way Maya was programmed. More such gems are waiting to be discovered when you dive into the world of expressions.
One more variable to set up and we're on our way. We need a variable to store a threshold value for the distance between the goal shape (a.k.a. locatorl) and the position of each particle. Since both of these attributes are vectors, our new measuring variable should be a vector as well. Type the following:
Vector values are expressed as <<x,y,z>>.
Now we come to the meat of the expression. We're going to create a simple conditional statement that says, "If the distance between the particle position ($pos) and the goal position ($goalPos0) is less than our distance measure ($dist), make the particle's radius .3. If not, make it 0." Here is the syntax:
Of course this does nothing unless we now reset the radiusPP attribute equal to the new $size value. So the last line is particleShape1.radiusPP=$size;
The syntax for finding the absolute value of a number is abs. If you remember from your high school math class, absolute value is the number itself without a negative sign. For example, the absolute value of -15.6 is 15.6, and the absolute value of 30 is 30. Since we work with a coordinate system that uses both positive and negative values, it's often necessary to get the absolute value of a position in order to measure distance in units properly.
Play back the animation. It should look kinda funky. The particles appear at the emitter, disappear, reappear near the locator, and then disappear again. Let's fix some of this.
- In the Expression Editor, switch back to creation expression mode, and set the particleShape.radiusPP value to 0. This will ensure that the particle size at birth is also zero and that we won't see the particles popping up around the emitter.
- That looks a little better. Let's expand the range of the visible particles. Go to the Runtime Before Dynamics mode and set our $dist variable to <<2,2,2>>.
- Play the animation again.
Much better. But it would be nice if the particles grew in size as they approached the locator and then tapered off as they passed by. We could set up a complex loop within our expression to calculate this; certainly if the animation becomes more complex, we would need to do so. However, let's hack our way through this by using a ramp. Ramps are easy to control and give us a nice visual aid in tuning our particles. It's not the most elegant solution, but it will do in a pinch.
- To use a ramp, we need to create a per-particle attribute to hold it. We'll then set the radiusPP equal to the ramp based on the lifespan of the particle. Select the particleShape1 node, then choose Modify Add Attribute. We are going to create our own custom per-particle attribute (see Figure 19.19).
- In the Attribute Name field, type sizeScaleRampPP. Set Data Type to Float, and set Attribute Type to Per Particle (Array). Leave everything else at the default. Click the Add button at the bottom of the panel and close the panel. You should see sizeScaleRampPP added to the list of array attributes.
- Right-click sizeScaleRampPP and choose Create Ramp. By default, the ramp's input V value is set to the particle's age based on its lifespanPP value. That's fine.
- Edit the ramp so that it has a line of white about three-quarters of the way from the top. The white should quickly transition to black at the top. See Figure 19.20 for some ideas.
- Now we have to plug our ramp into the expression. Go back to the Runtime Before Dynamics section of the Expression Editor and replace
Ssize=.3
Figure 19.19
You can use the New tab in the Add Attribute window to create your own custom attribute.
Figure 19.19
You can use the New tab in the Add Attribute window to create your own custom attribute.
with
Ssize=particleShape1.sizeScaleRampPP;
6. Play the animation. You should see a variation in size as well as a bit of ramping up and down in size as the particles approach and pass the locator.
Figure 19.20
The ramp controlling particle size over lifetime
Figure 19.20
The ramp controlling particle size over lifetime
You can tune this part of the effect in three ways:
- You can edit the array mapper's max value and set it to a number other than 1. Try 0.3 or 0.5. This means that the upper limit—the white part of the ramp—is now 0.3 or 0.5 rather than 1, so the scale of the particles can't go beyond this value.
- You can adjust the position of the colors on the ramp, even add some gray or white bars so that the size fluctuates more over time.
- Since the ramp is tied to the particle's age, you can adjust the lifespanPP values in the creation expression. The random range for the lifespan adds a level of randomness to the size of the particles since the values of the ramp are tied to each particle's age.
The effect looks good. However, we have a lot of flexibility built in that we are not taking advantage of. Since we're using a per-particle goal position to determine where the particles appear, we can add another goal and hence another level of control. Plus, there's no reason why we can't animate the position of these goals. First, let's add a goal.
- Save your work and then create another locator.
- Move this new locator (locator2) close to locator 1.
- Select the particle shape, select locator 2, and choose Particles ■ Goal.
- Open the Attribute Editor, and in the Goal Weights And Objects section, click the buttons under locator2 to add goalWeight1PP and goalWorldPosition1PP.
- Add a creation expression in the field for goalWeight1PP.
Remember, because of the order in which we added the goals, goalWeight0 and goalPosition0 refer to locator! and goalWeight! and goalPosition! refer to locator2. A little confusing. We probably should have named our locators more clearly before adding them.
- In the Creation Expression section of the Expression Editor, add a new line that reads as follows:
- goalWeight1PP=0;
- Switch to the Runtime Before Dynamics mode and add a variable for the new goal-WorldPosition1 attribute. The line should look like this:
vector SgoalPos1=particleShape1.goalWorldPosition1PP;
We have a new vector variable called SgoalPosl. Now we're going to change our expression, adding the new goal to the conditional statement. We will be saying, "If the absolute value of the distance between the per-particle position and the goal0 position is less than our distance variable or if the absolute value of the distance between the per-particle position and the goal1 position is less than our distance variable, use the ramp to control the size. Else, set the value to 0."
Here's the syntax:
If (abs($pos-$goalPos0<$dist)||abs($pos-$goalPos1<$dist)) $size=particleShape1.sizeScaleRamp; else $size=0;
Play the animation. Try moving locator2 closer to the emitter to make sure it's working. Remember that the particle is a stream moving in the positive X direction. If you move either locator too far out of the stream, you'll lose the effect.
Now things are looking rather neat (see Figure 19.21). For some additional fun, see if you can create some randomness in the distance variable so that the region around each locator grows and shrinks over time. This may require creating a float-variable, set to a random or noise function, and then plugging this new variable into one or more of the vector values of the $dist variable. Or you can use the sphrand function to generate random vector numbers and use that to alter the $dist value.
For one final touch, let's animate the position of the locators using an expression. Select locator1, right-click its translate Z field in the Channel Box, and choose Expressions. For the expression, type this:
- translateZ=sin(time); Do the same for locator 2, but set its Z value to the following:
- translateZ=cos(time);
Figure 19.21 The cloud scales as it approaches the locators.
Here is the syntax for the creation and runtime before dynamics expressions: Creation expressions:
particleShape1.lifespanPP=rand(5,6); particleShape1.goalWeight0PP=0; particleShape1.goalWeight1PP=0; particleShape1.radiusPP=0;
Runtime expressions:
float $size=particleShape1.radiusPP; vector $goalPos0=particleShape1.goalWorldPosition0PP; vector $goalPos1=particleShape1.goalWorldPosition1PP; vector $pos=particleShape1.position; float $noisy=(abs(noise(time)))+2; vector $dist=<<$noisy,$noisy,$noisy>>;
if (abs($pos-$goalPos0<$dist)||abs($pos-$goalPos1<$dist)) $size=particleShape1.sizeScaleRampPP; else $size=0;
particleShape1.radiusPP=$size;
The cloud(s/w) render type has a shader applied to it automatically when you create it. It's called particleCloud1, and you can find it in the Hypershade. Feel free to experiment with the settings and try rendering a few frames of the particle cloud in the scene. When you are satisfied with the look, create a particle disk cache and render the animation. A completed version of this scene, called cloud1.mb, is located on the CD.
This effect would look cool wrapped around a ghostlike figure. The controlling locators can be parented to parts of the figure itself, allowing the fog to come into existence as it approaches the figure.
Average user rating: 5 stars out of 2 votes
Post a comment