r13 - 29 Apr 2012 - 01:56:06 - MattEstelaYou are here: TWiki >  Maya Web > TWikiAdminGroup > MayaMisc > MayaModelling > MayaOptimisingRenders > MayaPaintFxAndHair > MayaParticles

Particles

How can I get more control over random particle colours?

Use the hsv utiltiies. This little example does variations on yellow, in a city lights kind of vibe.

$hsv = <<0.13, rand(1,0.1), rand(1,0.7)>>;
spritesShape.rgbPP = `hsv_to_rgb($hsv)`;

Here I'm setting the HSV components seperately:

H = 0.13. If the colour wheel starts at 0 as red, and 1 as red, 0.13 is sorta yellow (use the colour picker to help you here)
S = rand(1,0.1) meaning choose a random saturation between full saturated (1) to almost desaturated (0.1)
V = rand(1, 0.7) meanign chose a random value/brightness between 1 and 0.7.

hsv_particles.png

How do I make surface emitted particles stick to their surface?

Kept thinking I was over-engineering this and a lot would/should happen automatically, apparently not. You need to emit from surface (obviously), make the same surface a goal, add all relevant goal and parentUV properties, then set a creation expression linking it all together.

  1. emit particles from surface
  2. check emitter is in surface mode (often defaults back to omni), check 'need parent uvs'
  3. add general particle attributes goalU goalV parentU parentV goalPP
  4. set goal weight to 1
  5. set creation expression goalU = parentU; goalV = parentV;

See? Simple! Kinda. I wondered if there was an advantage to using this rather than just goalU = rand(1); goalV = rand(1);. Turns out there is; if your uv's aren't laid out evenly you'll get particles clustering on the edges of your uv borders. The 'proper' emit method above avoids this.

Make particle instances orient to surface normal

normal_instances.jpg

An extension of the above trick. Once you have your goal object defined, go into the 'goal weights' section of the particleshape, and click 'create goal world position 0 PP'. Create the shape(s) you wish to instance (remember the should point down the x-axis), create your instancer node, and set aim direction to goalWorldNormal0PP. This has the advantage of being quite fast, and will update if you deform or transform your goal object. Here's an example scene:

How do I uninstance particle objects ?

Unfortunately you can't simply duplicate the instancer to retrieve the mesh. Fortunately there are plugins & scripts around.

-- MattBernadat - 25 Nov 2011

How do I have some particles stick to a collision surface and some collide?

goalPP and modulus are your friends.

In creation:

// red particles will stick, black will bounce
 if (particleShape1.particleId %2 == 0)
    particleShape1.rgbPP = <<1, 0, 0>>;
 else 
    particleShape1.rgbPP = <<0, 0, 0>>;
 
 // initial goalPP setting, no attraction
 particleShape1.goalPP = 0;

In runtime: (all the collision PP attrs need to be added first for this to work)

if (particleShape1.particleId %2 == 0)
 {
 
    if (particleShape1.collisionTime != -1) 
    {
    particleShape1.goalU = particleShape1.collisionU;
    particleShape1.goalV = particleShape1.collisionV;
    particleShape1.goalPP = 1;
    }
 }
-- KevinMannens - 17 Sep 2008

How do I control ratio of particle instanced objects?

(adapted from a thread on highend)

First, have a look at the bottom of this page for info about the modulus (%) operator.

Based on this information, you should be able to understand the following MEL:

// loop through an array of 10
for ($x = 0; $x <= 10; $x++)
{   
// if the remainder is 0
// i.e. if $x is dividable by 10, print "One"
if ( $x % 10 == 0 )
    {
    print ("One\n");
     }

// If the remainder is smaller or equal to
// 2 AND if the remainder of $x is bigger then 0,
// then print 2
else if ($x % 10 <= 2  && $x % 10 > 0 )
     {
    print ("Two\n");
      }

// if the remainder is bigger then 2,
// print Three     
else
     {
        print ("Three\n");
     }
       
}

You could then write an expression to control the relative ration (e.g. 1-2-7) of particle instances:

if ( particleShape1.particleId % 10 == 0 )
          particleShape1.indexPP = 0; 

else if ( particleShape1.particleId % 10 <= 2  && particleShape1.particleId % 10 > 0 )
         particleShape1.indexPP = 1; 

else
         particleShape1.indexPP = 2; 

-- KevinMannens - 17 Sep 2008

How do I get particles to collide with only one side of a collision object?

(added by kmannens)

Brain teaser from cgtalk:

By default, particles in maya collide with connected surfaces on both sides not taking into account face normals or double-sided attribute. Is there a way or a node able to solve the one-sided particle-object collision?

Answer: You could do some fancy vector math, or you could be lazy and assign a white texture to one side of the object and black to the other side. Then use colorAtPoint and traceDepthPP. Like so:

//upon collision
if (particleShape1.collisionU != -1 )

{
// get collisionU
float $colU = particleShape1.collisionU;
//get collisionV
float $colV = particleShape1.collisionV;
// get the color at the UV
vector $colRGB = `colorAtPoint -o RGB -u $colU  -v $colV ramp1`;

if ($colRGB != <<1, 1, 1>>)
{
   //line below to fix collision evaluation problem
   particleShape1.velocity = particleShape1.collisionIncomingVelocity;
   particleShape1.traceDepthPP = 0;
}
else 
   particleShape1.traceDepthPP = 10;

}

How do I get (and use) the exact location of a particle collision?

(added by kmannens)

collisionWorldPosition is your friend. It's a PP attr that is a bit hidden, and I couldn't find any docs on it - but it does the trick. It does however, need another PP attr to work: collisionTime. So, set up your collisions as your normally would and then add collisionTime and collisionWorldPosition.

Say you want to create a locator at the point of collision.

In runtime, add something like this:

//get the collision position
vector $collPos = particleShape1.collisionWorldPosition;
// get collision frame
float $collFrame = (particleShape1.collisionTime*24);

//if there is a collision...
if (particleShape1.collisionTime != -1)

{   //verbose
   print ("particle ID " + particleShape1.particleId + " colliding at frame " +  $collFrame + ".\n");
   print ("Creating locator at: " + ($collPos.x) +" , " + ($collPos.y) + " , " + ($collPos.z) + ".\n");
   
   //create locator
   string $loc[] = `spaceLocator`;
   // center pivot on locator
   xform -cp $loc[0];
   
   // move locator to collision position
   setAttr ($loc[0]+".translateX") ($collPos.x);
   setAttr ($loc[0]+".translateY") ($collPos.y);
   setAttr ($loc[0]+".translateZ") ($collPos.z);


}

How do I place an annotation at the point of collision?

Say I want to have an annotation per collision that tells me the exact position in xyz of the collision. This turned out to be a bit trickier then expected. Thanks to Chris Armsden for helping me out with the syntax. The trick was not try and do it all in one go in the annotation command. Rather then using the built in -text and -position flags, I created a "naked" annotation first and then adjusted that node using setAttr. Like this:

vector $collPos = particleShape1.collisionWorldPosition;
float $collFrame = (particleShape1.collisionTime*24);

if (particleShape1.collisionTime != -1)
{   
   //########### locator stuff #################

   string $loc[] = `spaceLocator`;
   xform -cp $loc[0];

   setAttr ($loc[0]+".translateX") ($collPos.x);
   setAttr ($loc[0]+".translateY") ($collPos.y);
   setAttr ($loc[0]+".translateZ") ($collPos.z);

   //########### annotation stuff #################
   
   // create annotation   with dummy text and pos
   string $annotator = `annotate -tx "THIS SUCKS" -p 0 0 0`; //this returns a shape
   // get transform of annotation node, because command returns shape   
   string $annTrans[] = `listRelatives -p $annotator`;
   
   // position annotation at collision
   setAttr ($annTrans[0]+ ".translateX") ($collPos.x);
   setAttr ($annTrans[0]+ ".translateY") ($collPos.y+5);
   setAttr ($annTrans[0]+ ".translateZ") ($collPos.z);
   
   // cut of fractional part of collPos by re-declaring as int
   int $annX = $collPos.x;
   int $annY = $collPos.y;
   int $annZ = $collPos.z;
   
   // set the text and pos of annotation
   setAttr -type "string" ($annotator + ".text") ($annX + "|" + $annY + "|" + $annZ)  ;
   // parent annotation under locator
   parent $annTrans[0] $loc[0];

}

Is it possible that particles from one particle node have different effects on different collision objects?

(added by kmannens)

Interesting question on CGTalk, so I gave it a whirl:

The key to your success is an extremely obscure attr called collisionGeometryIndex (which not mentioned in the docs, but can be added with the general button as a PP attr), which provides you with an index of the pieces of geo the particles collide with. (Can be seen in the partShape AE under collision attrs)

Say you want the particles to go red when they collide with object1 and blue when they hit object 2:

int $idx = particleShape1.collisionGeometryIndex;
if( $idx != -1 )
{ 
   if ($idx ==1)
   particleShape1.rgbPP = <<1,0,0>>;
   if ($idx ==2)
   particleShape1.rgbPP = <<0,0,1>>;

}

It gets even more interesting if you want to extract the geo name from that. Thanks to Rob Pieke for the tip:

int $idx = particleShape1.collisionGeometryIndex;
if( $idx != -1 )

{ 
          string $geoC[] = `listConnections particleShape1.collisionGeometry[$idx]`;  
          string $shape[] = `listConnections ( $geoC[0] + ".localGeometry" )`; 
          print( $shape[0] + "\n"); 
}

make particle expressions easier to edit

my forehead-slapping obvious tip of the day. if your tweaking expressions a lot, you end up staring at a dense expression editor like this:

particlesLowerShape.dU = (1+rand(-particlesLowerShape.uSpeedRange, particlesLowerShape.uSpeedRange)) * particlesLowerShape.uSpeed * 0.017;
particlesLowerShape.goalOffset = particlesLowerShape.goalWorldNormal0PP * (( noise(particlesLowerShape.particleId * particlesLowerShape.offsetSpeed + time) - 1));

you can refer to variables by the local name (say uSpeedRange rather than particlesLowerShape.uSpeedRange), but each time you update the expression, maya fills in the full name again. Instead, close the expression editor, and rename your particleShape to a single character, say 'p'. Your expression now looks like this:

p.dU = (1+rand(-p.uSpeedRange, p.uSpeedRange)) * p.uSpeed * 0.017;
p.goalOffset = p.goalWorldNormal0PP * (( noise(p.particleId * p.offsetSpeed + time) - 1));

Much easier to read! You can even leave it like this if you like, but if you have multiple particleshapes in a scene, you might want to rename it after you finish the expressions, just to avoid naming conflicts later on.

Instance rotation mel

I'd always planned to write a little melscript to make particle instance rotation easier. Kevin Mannens beat me to it. smile

Scaling emission rate based on emitter speed

(This time I added it myself! - kmannens)

This is based on a reply I gave to a question on cgtalk. The question was: How to scale particle emission rate based on the velocity of the surface emitter?

1. Create one particle (call it velocityParticle) with the particle tool and goal (with goalweight 1 so it snaps immedately) it to the animated emitter

2. Add a PP attribute called speed and have this in runtime of the velocityParticle:

velocityParticleShape.speed = mag (velocityParticleShape.velocity);
if (velocityParticleShape.speed < 15)
emitter1.rate =0;
else 
emitter1.rate =100

(Change the expression to get the rate you need)

3. After you goaled your particle to the emitter (or whatever transform), you might want to do a Set Initial State.

4. (Optional) If you have a lot of emitters, you could try something like this:

{
string $emitter[] =`ls -sl -type "pointEmitter"`;
for ($eachEmitter in $emitter)
{
vector $emitterPosition = `xform -q -t -ws $eachEmitter`;
float $posX =  $emitterPosition.x;
float $posY =  $emitterPosition.y;
float $posZ =  $emitterPosition.z;

string $createP[] = `particle -p $posX $posY $posZ`;
xform -cp $createP[0];
goal -w 1 -utr 0   -g $eachEmitter  $createP[0];
}
}

Change particle colour on collision

Again via mr particle, Kevin Mannens:

global proc ParEvent(string $ParName,int $ParID,string
$ObjName)
{
print("ObjName:"+$ObjName+"\n");
   
//Get Par Position 
vector $ParPos;
$ParPos=`particle -id $ParID -at position -q
$ParName`;

//convert part pos into UV
setAttr cps.inPositionX ($ParPos.x);
setAttr cps.inPositionY ($ParPos.y);
setAttr cps.inPositionZ ($ParPos.z);
   

//get UV
float $U = `getAttr cps.parameterU`;
float $V = `getAttr cps.parameterV`;
   
//get color at collision UV
vector $Color=`colorAtPoint -u $U -v $V -o RGB file1`;

float $R=$Color.x;
float $G=$Color.y;
float $B=$Color.z;
      
//assign color of collision UV to particle
particle -e -id $ParID -attribute rgbPP  -vectorValue
$R $G $B $ParName;

};

Uber-Mayan Tom Kluyskens hinted me (Kevin Mannens) to a more elegant and faster way of achieving the same effect.

1. Set up a collision between particle and the textured surface as normal. (in the example scene I have just used a plane with a ramp on it)

2. Create a particle collision event

3. Add collisionU and collisionV as PP attributes

4. Add a custom PP attribute called tempColorPP

5. Create a ramp on tempColorPP, but click on the option box and set these options for the array mapper

inputU -> collisionU inputV -> collisionV Map To -> (the name of the ramp you have applied on your collisoin surface)

Most of the time, you wont use a ramp, but a file texture. To use a texure instead of a ramp, give your ramp only one color input, and map your texture to that color.

6. Finally, create this runtime expression on rgbPP:

if (particleShape1.collisionU>0) 
particleShape1.rgbPP = particleShape1.tempColorPP;

Driving softbody goals via textures

Adapted from this thread on cgtalk, the solution coming from Kevin Mannens:

Use an array mapper on the goal PP. When you create a ramp on the goalPP, set these settings:

* inputU: goalU

* inputV: goalV

* Map to: the ramp you have mapped on your surface (you will need to use a ramp, but you can connect a texture into one of the ramp swatches if you need a texture instead).

In the creation expression I have this:

particleShape1.goalU = particleShape1.parentU;
particleShape1.goalV = particleShape1.parentV;
//I added this to show the independent colorization of the particles 
particleShape1.rgbPP = <<rand(0,1),rand(0,1),rand(0,1)>>;

Here's 2 examples scenes Kevin created, in goalPP2.mb I keyframed the ramp so you can see the goalPP being affected.: texGoalPP.zip

Particle intercollision

Kevin Mannens via highend3d:

My preferred way to doing that is using the “use selected as source of field” functionality. Basically what you do is create a negative radial field with a small max distance and use each individual particle as source for that radial field. In doing do, particles will repel each other – as if they have opposing magnetic fields. For this to work, you need to tick on “apply per vertex” in the fields AE.

Matte opacity for volume particles

Duncan via highend3d:

The particle volume render does not support matte opacity (to my knowledge). While particle volume shaders seem the same as other volume shaders, internally they are handled a bit differently. The fluids shader is modified internally to handle the case when it is invoked for particle rendering( the fluid shader is the only shader other than the particle volume shader than can render particle volumes in Maya ). The fluid shader does handle matte opacity, so I tried it with particles, however the matte opacity does not work in this context. Thus as far as I can tell you are out of luck... no shading network will make this possible. The shading engine ignores matte opacity in the particle volume shading context.

As a workaround you can render an extra pass, setting colors to your desired matte values, in order to get the matte you want.

Vibrating particles on collision

Using a volume axis turbulence with a snow sim made the particles vibrate on the floor despite doing a bunch of event tests. Turns out the expression I needed on runtime after dynamics was

if (particleShape1.event > 0) {
   particleShape1.mass = 6000;
}

Not exactly elegant, but it works.

Animation takes with rigid body sims

Not realy particles, but I'll put it here while I think about making a dynamics page or not... Anyways, brilliant tip from Adrian Graham, thanks Adrian!

You can't cache to disk, but you can do multiple animation takes by baking the rigidBodies and using the choiceNode that's created.

Once you bake a rigidBody, look in the graphEditor and you'll see a 'Bake Simulation Index' curve. Your first baked animation will set the curve's value to 1, but if you set it back to -1, you can re-simulate and bake again, and it'll add to the index each time. Do 10 different 'takes', and you can hop back and forth among all of them by adjusting the index.

Hypervoxels or afterburn for maya

Someone on the highend list linked to some great work by Peter Shipkov. He's thrown together a rig of particles, fluid shaders, sampler nodes and a bunch of other stuff to get a pretty cool emulation of afterburn, but in maya. You get the look of fluids, but with the control (!) of particles. His videos are neat:

http://petershipkov.com/development/afterburn/afterburn.htm

Lots of other really nice experiments on his site, have a browse.

http://petershipkov.com/

Emit particles from outside of a ring

Question from gary@fuzzygoat, answer by Elvira Pinkhas.

You can write an expression so that the particles have lifespanPP=0 when they are within the boundaries of the circle. The equation for a circle is x^2+y^2=radius^2

I just tried the followitng and it worked; I created a circle of radius 1 and used a curve emitter, with 0 in direction X. Set the lifespan mode to lifespanPP.

Now the following expressions.

Creation:

particleShape1.lifespanPP=5;  
(or however long you think they should live)

Runtime:

vector $pos = particleShape1.position;
float $radius = sqrt(($pos.x*$pos.x)+($pos.z*$pos.z));
if ($radius<=1)
 particleShape1.lifespanPP=0;
else
 particleShape1.lifespanPP = 5;

solidTexSampler

Go download this plugin right now. It allows you to affect just about any particle property with a 3d texture or projected 2d texture. A perfect example of this is the x-men pushpin map. Create a particle grid, run jSolidTexSampler (a little melscript that creates the node and assigns a default ramp), and add the following expression:

vector $pos = particleShape1.position;
vector $img = solidTexSampler1.colors;
particleShape1.position = <<$pos.x, $img.y, $pos.z>>;

Rotate the 3dTexturePlacement node so it projects from the top, go 'ooooh'. Its one of those plugins that should be included in maya as standard, or Julian Mann should charge lots of money for (but don't tell him that).

Seems Julian got too busy to keep working on it, so he released the source code. If you have the l33t skillz to compile this and upload a copy to highend3d, I'll be your best friend... source here: http://www.highend3d.com/maya/downloads/plugins/rendering/misc/2937.html

Random particle rotation

Someday I'll make a melscript for this. In the meantime, here's one way to make particle instances rotate in individually set random directions and speeds.

1. Create new custom dynamic attributes for your particle:

particleShape1.rotMax (scalar, float) 
particleShape1.axis (float PP) 
particleShape1.rot (vector PP) 
particleShape1.rotMaxRand (float PP) 

2. CREATION expression:

particleShape1.axis = floor(rand(3)); 
particleShape1.rot = <<rand(360),rand(360),rand(360)>>;  
particleShape1.rotMaxRand = rand( 0 - particleShape1.rotMax, particleShape1.rotMax) / 1000; 

3. RUNTIME expression:

if (particleShape1.axis == 0) 
    particleShape1.rot = particleShape1.rot + << particleShape1.rotMaxRand, 0, 0 >>; 
else if (particleShape1.axis == 1) 
    particleShape1.rot = particleShape1.rot + << 0, particleShape1.rotMaxRand, 0 >>; 
else 
    particleShape1.rot = particleShape1.rot + << 0, 0, particleShape1.rotMaxRand >>; 

4. In the Att Editor for particleShape1 -> Instancer -> Rotation Options -> Rotation = rot

  • Change the value of particleShape1.rotMax to control the speed of rotation. Each instance will start out with a random rotation, and each will rotate in a different direction at its own speed.
  • If consistency is a problem, like if it never plays back the same way twice, you may need to make a general expression using the "seed" function. For example:
if (frame == 1) 
seed(10); 

Instanced particles and rotation

It looks like using degrees for instanced particle rotation doesn't work. Use radians instead. For example, the creation expression to set particles to either 0 or 90 degrees on y would look like

particleShape1.rot = <<0, trunc(rand(0,2))*1.57, 0>>

Because 180 degrees = pi radians, 90 degrees is pi / 2 or about 1.57. Then when you create your instance, set the rotation mode to radians.

Making instanced particles scale over time

Handy way to scale instanced objects, with controls over speed.

1. Select your particleShape (not the transform!), add a float attribute called scaleStep, with a default of 0.1

2. Again on the particleShape, create a PP vector attribute called scalePP.

3. Add the following runtime expression:

if (particleShape1.scalePP <= <<1,1,1>>) {
    float $scaleStepScaled = particleShape1.scaleStep * 0.1;  // cheers dave.
    vector $temp = particleShape1.scalePP;
    particleShape1.scalePP = <<$temp.x + $scaleStepScaled,$temp.y + $scaleStepScaled, $temp.y + $scaleStepScaled>>;
}

4. And this creation expression to set the scalePP to 0:

particleShape1.scalePP = <<0,0,0>>;

5. In the geometry instance controls, set scale to be scalePP.

Render every 5th particle

We can use modulus (%) on the particle id to work out which ones to kill. Set lifespan type to be lifespanPP only, and use this runtime expression:

if (particleId % 5 != 0) lifeSpanPP = 0;

Modulus? Wtf??

Modulus is a cooler word for remainder. Remember division in primary school?
  • 5 / 2 is 2, with remainder 1.
  • So 5 % 2 = 1.

Because 5 goes into 5 exactly with no remainder:

  • 5 % 5 = 0

So does 10, 15, 20, 25 etc... thats how we get every 5th particle.

To illustrate this, execute the following:

// loop through an array of 10
for ($a = 0; $a <= 10; $a++)
{
    float $r = $a % 10;
    print ("The remainder of " + $a + " is " + $r + ".\n");
   
}

This should give you:

The remainder of 0 is 0.
The remainder of 1 is 1.
The remainder of 2 is 2.
The remainder of 3 is 3.
The remainder of 4 is 4.
The remainder of 5 is 5.
The remainder of 6 is 6.
The remainder of 7 is 7.
The remainder of 8 is 8.
The remainder of 9 is 9.
The remainder of 10 is 0.

See above for a more advanced (expression) example with particle instances -- KevinMannens - 17 Sep 2008

-- MattEstela? - 31 May 2003

Topic attachments
I Attachment Action Size Date Who Comment
pngpng hsv_particles.png manage 87.4 K 12 Jan 2012 - 06:30 MattEstela  
elsemel km_randomInstanceRotation.mel manage 3.0 K 12 Dec 2005 - 01:26 MattEstela  
jpgjpg normal_instances.jpg manage 29.9 K 26 Aug 2009 - 18:14 MattEstela  
elsema normal_orient_instances.ma manage 129.7 K 26 Aug 2009 - 18:11 MattEstela  
zipzip texGoalPP.zip manage 14.8 K 27 Oct 2005 - 00:04 MattEstela  
Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r13 < r12 < r11 < r10 < r9 | More topic actions
 
Powered by TWiki
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback