[maya] particleSamplerInfo smoke

In the past few years I have often been asked to create smoke for a variety of different visual effects. There are many ways to do it in maya using particles or fluids so I choose one that suits the job. Last week I needed to use particles and that led me to an interesting discovery - how to use the particleSamplerInfo node.

I had seen it before, in the hypershade Create menu, but I'd never tried to use it. But then I stumbled upon this archived post on cgtalk where Anders Egleus said

I've recently tested rendering particle clouds in mentalray instead of maya software and I'm very happy with how mentalray shades and motion blurs particle clouds.
...
It seems that turning on the local attribute in the 3d texture doesn't seem to have any effect when rendering with mental ray. No matter how I try, the particles seem to "swim" through the texture.

For years I've been rendering particle clouds (with the software renderer) and I didn't realise that I could turn on the local attribute in the effects tab of the texture node to stop the particles swimming through the texture. I've always done things like animating the place3dTexture node or the texture's time attribute to try and hide it, but was never totally satisfied with the results.

These days I use mentalray for most things and I also like the way it shaders particle clouds, so I wanted to fully understand the rest of Anders' post. Read on to see where it got me. (I'm very happy with the result).

I'll limit my explanation to one specific example and I'm going to assume you know a little about using particles in maya.

Overview:

Anders Egleus figured the math behind maya's 3D Texture called Volume Noise. Knowing that math enables us to achieve the same result as what the local flag does when you use the software renderer. This means we can use Volume Noise as the blob map in the Particle Cloud shader, render it with mentalray, and the texture will move and scale with the particles completely negating the swimming effect.

Here is a movie that demonstrates the difference (mp4 h.264 or mov sorenson3)

The smoke on the left obviously swims though the texture, but the one on the right does not.

You can download an example scene file here (maya2008 sp1 .ma)

How to do it:

I started by creating a directional emitter which has an animated z rotation thanks to this expression

emitter1.rotateZ = 20*noise(frame/10);

The Particle Render Type is Cloud. Lifespan Mode is lifespanPP only.

I created a new Particle Cloud shader (Hypershade, Create Maya Nodes, Volumetric, Particle Cloud) and assigned it to the particles. I created a Volume Noise texture (Hypershade, Create Maya Nodes, 3D Textures, Volume Noise) and deleted the place3dTexture which is not needed in this example. Then I connected the outColor of the volumeNoise to the blobMap of the particleCloud.

Ok, now for the complicated part.

The particleSamplerInfo node does for particles what the more widely known samplerInfo node does for surfaces. It lets us extract data from the particles that we can use to control parts of our shader network. In this example I want to use each particle's position and radius to control what part of the volumeNoise texture is used and what the frequency should be.

Unfortunately not all the particleSamplerInfo outputs work in mentalray, so we need to use Anders' trick to get the data we need. We can use the expression editor to redirect the data we want to one of the supported outputs. In this example I will reroute the particle position (x,y,z) to the rgbPP, and the radiusPP will be sent out via the incandescencePP. (Wierd way of working, but it gets the job done!)

So particleShape needs these attributes to be added: radiusPP, incandescencePP, rgbPP.

The math that Anders figured out is this

origin = -1 * position * frequency / (scale * 16)

Using the particle position with this formula to set the volumeNoise origin attribute makes the texture follow and scale with the particle.

But I don't want every particle to get the same part of the texture, so I added a per particle offset to the equation. To implement this I had to add a custom particleShape attribute - a per particle vector called originOffset.

To make the scale of the volumeNoise follow the size of the particle we use

frequency = 1/radiusPP

But we also want to be able to control the overall frequency and animation speed so I added two extra attributes to the volumeNoise called frequencyMult and timeSpeed.

Hooking it all together.

First I wrote a creation expression for the particleShape.

// CREATION
//
// originOffset is a custom per-particle vector attribute
// that we use to store a random position offset
//
particleShape1.originOffset = sphrand(1);

// creation radiusPP
//
particleShape1.radiusPP = rand(0.1,0.3);

// lifespanPP
//
particleShape1.lifespanPP = rand(1,3);

// particleSamplerInfo:
//     use outIncandescencePP.r to output 1/radiusPP
//
particleShape1.incandescencePP = <<1/particleShape1.radiusPP,0,0>>;

// particleSamplerInfo:
//     use outColor to output position (including originOffset adjusted for radiusPP)
//
particleShape1.rgbPP = particleShape1.position + (particleShape1.originOffset * particleShape1.radiusPP);

And then a runtime expression

// RUNTIME AFTER DYNAMICS

// increase radiusPP
//
particleShape1.radiusPP = 1.03 * particleShape1.radiusPP;

// particleSamplerInfo:
//     use outIncandescencePP.r to output 1/radiusPP
//
particleShape1.incandescencePP = <<1/particleShape1.radiusPP,0,0>>;

// particleSamplerInfo:
//     use outColor to output position (including originOffset adjusted for radiusPP)
//
particleShape1.rgbPP = particleShape1.position + (particleShape1.originOffset * particleShape1.radiusPP);

In the creation expression I set the radiusPP to be within a random range, and then in the runtime expression I added a multiplier of 1.03 to make the radius grow as the particle get older. My aim is to keep the particles overlapping as they move apart. Here's some pictures to show what I mean - first with constant radiusPP

particleWire_noGrow.jpg

and then with the 1.03 growth factor applied

particleWire_yesGrow.jpg

This way the smoke will look like it is expanding to fill the volume, rather than just separating into little puffs.

The comments in the expressions indicate what each line is there for, so I will leave it at that. The expression sets the particleSamplerInfo.outColor to be the particle position (with a random offset for each particle) and the particleSamplerInfo.outIncandescence will be set to the inverse of the radiusPP (I use just the red component). These are the outputs that we hook up to the volumeNoise, but to achieve the correct math we also need a network of multiplyDivide nodes.

I renamed each multiplyDivide node to indicate its function because even though the math is quite straight forward, it gets confusing when you try to keep track of all the nodes. Notice that one of these simply multiplies the particleSamplerInfo.outColor by -1/16 which is -0.0625, but attributes are displayed with only three decimal places so it looks like -0.063. As long as you type in the correct number it will still work properly.

Here is a diagram with connection details overlaid which shows the shader network I created. Click the picture to see a full size version in a new window.

hypershadeNetwork_small.jpg

I used the connection editor to make all the connections and now the texture will stick to each particle.

Next I wanted to have the particle become more transparent as it gets older, so that it will be invisible by the end of its life. You can do this easily by connecting a ramp to the transparency of the particleCloud. Maya will automatically hook up a new particleSamplerInfo to the ramp's uv coords, but I decided to use the one I already had and hooked it up manually as shown here.

hypershadeTransLife.jpg

Here are screen shots of the attributes I used to render the example movie.

volumeNoiseAttributes.jpg

particleCloudAttributes.jpg

transLifeAttributes.jpg

The look of the particleCloud can be varied considerably by playing with combinations of attributes. Experimentation is required to really see how everything works together.

Summary:

Each particle's radius will determine the volumeNoise frequency. Its position and offset will determin the volumeNoise origin. And it's age will determine the volumeNoise time, so the noise will evolve with the particle's life. There are two extra attributes on the volumeNoise which can be used to set a multiplier for the noise frequency and for the noise time. As a result the volume noise moves with the particles.

Notes:

volumeNoise has several Noise Types but not all of them obey Anders' formula. These do: wispy, perlin, spaceTime. These don't: billow, volumeWave - although its not really a problem in practice. If you use billow, then the texture will not move exactly in sync with the particles, but it will still move and evolve and any texture swimming will probably not be noticeable.

While I'm on this subject, I think it is appropriate to link to Peter Shipkov's Over Burn (formely known as After Burn). He has taken an idea similar to this, but to a whole new level of complexity using maya fluids.

You can download an example scene file here (maya2008 sp1 .ma)

'공부 > Maya' 카테고리의 다른 글

[maya] Dynamics-Particle Basic  (0) 2011.01.28
[maya] Ocean Spray  (0) 2011.01.26
[maya] Blast Code Setting  (2) 2011.01.10
[maya] ice cracking  (0) 2011.01.10
Dynamics tutorial 모음집!!!!  (0) 2010.07.26