Tuesday, 17 June 2014

Passing rgbPP to Instanced Objects

This is a nice one that I have needed quite a lot in the past. Now, thanks to Arnold, it's extremely easy to do.

Here is the problem:

I want to vary the shading on objects that are instanced to a particle system.

In Maya and Mental ray this is not easy to do. In fact I don't know of any way to do it. In Arnold, however, it is very straightforward:

1. Create your particle and instancer system as you normally would do.

2. Assign a shader to your instanced objects (not the instancer object)

3. create an aiUserDataColor node

4. type rgbPP in the Color Attr Name in the aiUserColor node

5. connect aiUserDataColor.outColor --> diffuse in your shader (or whatever channel you need it to go to)

6. type rgbPP into the Export Attributes in the Arnold section of the particle object.


 That's it. A really easy and long overdue feature.









Friday, 13 June 2014

Passing ageNormalized to Arnold

Passing the ageNormalized attribute from Maya particles to an Arnold shader is extremely useful and extremely not documented in Solid Angle's user guides. I will show two ways to do it - one is my own recipe, and one of from Pedro Gomez from the MtoA list.

Here is the problem:


I would like to pass my particle's ageNormalized to a shader, rather than age.

As you may be able to see from the screenshot, passing age sort of works, but not quite. Some of the oldest particles have reached the end of the colour ramp and wrapped around to the beginning of the ramp again.

If I try just typing in ageNormalized into the Export Attributes, it does not work at all, Arnold just reads the first value of the ramp and applies that value to every particle.

Is there a smart workaround for this? Can I put age/lifespanPP somewhere in the shader? But where? And talking of export attributes, can I put more than one attribue in there (eg age, lifespanPP, velocityPP)? 







First is my method: 

1. Add a new Dynamic Per-Particle Attribute, userScalar1PP, say.

2. Adding the runtime expression:

nParticleShape1.userScalar1PP=nParticleShape1.age/nParticleShape1.lifespanPP;


3. Put userScalar1PP into the Export Attributes

4. Connect the particle sampler to the shader ramps, but use the UserScalar1PP attribute instead of age.






Second is Pedro's way - more correct and elegant:

Export both the age and lifespanPP and catch those in two aiUserDataFloat nodes. Then use a Multiply/Divide node and divide the age/lifespanPP. Then pipe that into your shader. This is a much better as it does not require an expensive runtime expression to be cached.



Here is the shader: one ramp for Colour and one for Opacity.




And the answer is yes, you can export any number of attributes, so long as they are seperated by a space in the Export Attribues box.

UV particles via SOuP

Here is a small SOuP technique for producing a plane of particles with a UV gradient on their RGB.

First you need a plane, then emit some particles from that plane. Make sure that the particles have the rgbPP attribute available as we will need to put an expression on it.

Create a SOuP TextureToArray node
Create a SOuP rgbaToColorAndAlpha node
Create two ramp texture nodes - ramp1 is black to red along U, ramp two is black to green along V

Connect the following:

ramp2.outColor -->  ramp1.colorOffset
ramp1.outColor --> textureToArray1.inColor
polyPlaneShape.worldMesh[0] --> nParticleShape.inputGeometry
polyPlaneShape.worldMesh[0] --> textureToArray1.inGeometry
textureToArray1.outRgbaPP --> rgbToColorAndAlpha1.inRgbaPP 

Also connect the emitting plane's transform node to the particle's transform node as shown in the node graph.



Now set the rgbPP using the creation expression:

rgbPP=rgbaToColorAndAlpha1.outRgbPP

Rewind and step forward one frame so that the particles are emitted. Then set their initial state and disconnect the emitter and the connection between polyPlaneShape.worldMesh[0] --> nParticleShape.inputGeometry





The particles will now be dynamic again.


In Nuke, plug in your rendered particles into the STMap node as shown in the image below


GPU renderers

I will be testing some particle renderers in the next few days - Arnold, Fury and Krakatoa.


This first test is Fury

20 million nParticles
motion blur switched ON
4 x multisampling

13.26 seconds (dual Xeon E5 - 32 cores, 48GB RAM, Nvidia Quadro 4000)

I'm showing the alpha channel only because I currently just have the demo version of Fury and the watermark is distracting in the colour channel.


The next test is Krakatoa

motion blur OFF, render time 11 seconds

It's slightly trickier to get started with Krakatoa, but I think the results look amazing. Again, this is a lot of particles (14 million)


Here is my setup for Arnold, but I cannot seem to get the opacity to work properly.






More details as soon as I can get some help making this work.

I have finally got this working. Please see my later post "passing ageNormalized to Arnold"

Friday, 26 October 2012

nCloth instances

I have been working on a confetti shot and wanted to use nCloth. I decided to use a particle emitter and to instance the nCloth object onto the particles - but how? If I were to simply assign the nCloth object to the instance, then each instance would be identical. I wanted to have a different starting point in the nCloth cache for each instance. Unfortunately Maya's instancer doesn't support this kind of connection. I tried to use an userScalarPP attribute along with a particle sampler to access the cacheStart attribute on the nCloth cache, but that didn't work.

Here's how I did it:

First cache the nCloth object

Export the nCloth as a sequence of OBJs. I used a python script called objsExporter_v2 from Christos Parliaros

Re-import the OBJ sequence using Dave Girard's objSequenceImporter

Use the imported OBJs to create an instancer with cycling set to On

On the particle object I setup a per-particle attribute; cyclePP and used this to select the correct OBJ in the sequence. For example, if there are 30 OBJs in your sequence, in the creation expression:

cyclePP=0;

and in the Runtime before dynamics:

cyclePP=(cyclePP+1) % 30;

so the nCloth object will loop through the cache and start from the beginning at the end of the loop.

Tuesday, 3 January 2012

Maya Strands*

*OK, not exactly the same as Softimage ICE strands (which are great, by the way), but here is a way to get a very basic approximation.

I am basically taking Sigillarium's particle expression for making a uniform trail of particles and adding a line to take the seed particle's colour attribute and passing it to the emit command. This way, the trails have the same colour as the emitter particle, which is very handy if you are emitting from a surface and inheriting particle colour from that surface.

Here is the expression:

//runtime before dynamics

seed_particleShape.beforePosition = seed_particleShape.position;



//runtime after dynamics

string $trail_pt = "smoke_nParticle";

float $separ = seed_particleShape.separation;

vector $lastPos = seed_particleShape.beforePosition;

vector $pos = seed_particleShape.position;

vector $move = <<(($pos.x)-($lastPos.x)), (($pos.y)-($lastPos.y)), (($pos.z)-($lastPos.z))>>;


//get colour info

vector $rgb=seed_particleShape.rgbPP;

float $r=$rgb.r;

float $g=$rgb.g;

float $b=$rgb.b;


//get number of particles to emit per frame

int $num = ceil( mag( $move ) / $separ );


//loop !

if( $num != 0 ) {

vector $step = $move / $num;

for( $i = 1; $i <= $num; $i++ ) {

vector $newPos = $lastPos + $step*$i;

float $life = time - (1.0/25/$num * ($num-$i));

emit -o $trail_pt -pos ($newPos.x) ($newPos.y) ($newPos.z) -at rgbPP -vv ($r) ($g) ($b) -at "birthTime" -fv $life;

}

}



There are a couple of things to note:


The seed particle shape node has an extra attribute added - separation. This is a float value that determines the distance between each particle in the trail. The number you use depends on the scale of the scene and velocity of the seed particle. It's handy to have this variable on the shape node rather than in the expression.


There is a per-particle vector attribute called beforePosition, which stores a particle's position from the previous frame. This attribute needs to be created using the Add Attribute dialogue:





Also, note the variable $trail_pt in the expression. This is just the name of the trail particle object. Create this object before running the expression either by a 'particle' or 'nParticle' MEL command or by creating an emitter via the menus and deleting the emitter and leaving the particle object behind. Set the $trail_pt variable to the name of your trail particle object.






So, you can hopefully see that I have an image on a plane from which I am emitting some particles which are taking the colour from the plane. Those particles are then emitting more particles in a trail and inheriting the colour from the first particles.

Thanks to Sigillarium for the excellent expression. Please check the Sigillarium blog as it is outstanding and very clearly explains some difficult concepts.

Tuesday, 29 November 2011

Motion Vectors for hardware particles

I am trying to write an expression which will mimic the mv2DToxik motion vectors, but for hardware particles. Therefore removing the need to instance some geo to get the motion vectors to render.

I have not got very far before I came across some vector maths...


Here is the setup which works for a camera pointing exactly down the z-axis.







Here is the render loaded into Nuke. Notice the RGB values.



I now need to find a way to convert World Velocity to Screen Space Velocity.
In a MEL expression. Hmmm, time to ask the Forum

After some great advice from Zoharl, I grabbed some code which uses the camera's worldInverseMatrix to transform the velocity vector.

Here is the expression:

//multiplier
float $mult=0.5;

//get the particle's World Space velocity
vector $vel=particleShape1.worldVelocity;
float $xVel=$vel.x;
float $yVel=$vel.y;
float $zVel=$vel.z;

// create particle's velocity matrix which is in World Space
matrix $WSvel[1][4]=<<$xVel,$yVel,$zVel,1>>;

// get the camera's World Inverse Matrix
float $v[]=`getAttr camera1.worldInverseMatrix`;
matrix $camWIM[4][4]=<< $v[ 0], $v[ 1], $v[ 2], $v[ 3]; $v[ 4], $v[ 5], $v[ 6], $v[ 7]; $v[ 8], $v[ 9], $v[10], $v[11]; $v[12], $v[13], $v[14], $v[15] >>;

//multiply particle's velocity matrix by the camera's World Inverse Matrix to get the velocity in Screen Space
matrix $SSvel[1][4]=$WSvel * $camWIM;

vector $result = <<$SSvel[0][0],$SSvel[0][1],$SSvel[0][2]>>;
float $xResult = $mult * $result.x;
float $yResult = $mult * $result.y;
float $zResult = $mult * $result.z;

//rgbPP
particleShape1.rgbPP=<<$xResult,$yResult,0>>;


So far it seems to be working, but I will try to test it and see if it breaks down.





Thanks to Zoharl on the CGTalk forum and to xyz2.net and 185vfx who came up with the original matrix manipulation code.