# particles to follow a sine-wave shape?

## Recommended Posts

hi,

how can I make particles to follow a sine-wave shape?

I understand that inside object or SOP level I can use expression sin(\$F) to get that kind of motion for objects, but how to do that in POPs?

Is there any pre-built Force or Behavior, or should I use VOP POP?

In VOPs, under Patterns there's nothing like Wave or Sine.

My bet is on Trigonometric Functions VEX node or Oscillations VEX node, but don't know how to use them and I can't find any example.

Also, when I use Turbulent Noise in VOPs to modify particle position it seems like velocity vector is not updated. Why is that?

cheers,

nem

Edited by watchman
##### Share on other sites

hi,

how can I make particles to follow a sine-wave shape?

I understand that inside object or SOP level I can use expression sin(\$F) to get that kind of motion for objects, but how to do that in POPs?

Is there any pre-built Force or Behavior, or should I use VOP POP?

In VOPs, under Patterns there's nothing like Wave or Sine.

My bet is on Trigonometric Functions VEX node or Oscillations VEX node, but don't know how to use them and I can't find any example.

Also, when I use Turbulent Noise in VOPs to modify particle position it seems like velocity vector is not updated. Why is that?

cheers,

nem

hmm very easy - pop net -> location (birth : impulse birth rate = 1, vel = 0, vel variance = 0) -> position POP

in position POP:

tx = \$PT

ty = sin(\$PT*10)

for example)

##### Share on other sites

but I still have trouble with what I want to achieve:

1. with positionPOP velocity vector is not updated. (but when particle position is modified by i.e gravityPOP it's vector is updated properly.)

2. birth location is also altered with positionPop so particles doesn't look like being born from a single point. (reminds me on maya creation vs. runtime expression.)

3. wave is limited to one axis in coordinate space i.e Y axis, but I want the wave to be along particle's current (arbitrary) direction of movement.

maybe this pictures can explain my problems.

the last image should illustrate the ultimate goal of this exercise.

Edited by watchman
##### Share on other sites

but I still have trouble with what I want to achieve:

1. with positionPOP velocity vector is not updated. (but when particle position is modified by i.e gravityPOP it's vector is updated properly.)

2. birth location is also altered with positionPop so particles doesn't look like being born from a single point. (reminds me on maya creation vs. runtime expression.)

3. wave is limited to one axis in coordinate space i.e Y axis, but I want the wave to be along particle's current (arbitrary) direction of movement.

maybe this pictures can explain my problems.

the last image should illustrate the ultimate goal of this exercise.

This is actually a classic calculus problem. Basically you have a particle whose position is a function of time, and you want to find it's velocity, which is just the first derivative of that function with respect to time. So, let's start out with the simple case of a particle moving along the x-axis, where the y position is such that it forms a sine wave:

x = F * t

y = A * sin(x)

where F and A are constants to control the frequency and amplitude of the wave.

so, take the first derivative of each of these with respect to time:

dx/dt = F

dy/dt = A * F * cos(F * t)

These are just the x and y components of the particles velocity vector. If you plugged these into a VOP or Velocity expression you'd get the proper behavior along the x-axis.

OK, but you want it to work along any axis. So you can set the emitter to emit with whatever velocity variance you like, and set it to put all just-emitted particles into a group, called "birth" or whatever. For those just-born particles, create an attribute "initial_v" and store that initial velocity. Then you can find the rotation matrix that transforms the x axis to that initial velocity (using the Orient VOP or the "dihedral" function), and multiply the x-axis case from above by that matrix to get the result along an arbitrary vector.

You could just generate a random rotation vector yourself, of course, if you'd prefer. Also note that all these formulas should use each particle's age, not the global time. You could offset that age by a random amount or something if you're getting too much uniformity, vary the freq and amplitude per partice, etc.

Attached is an example. I did this in a VOP network, but you could certainly do it with expressions. It would be slower, however. I put a Trail SOP on this to make it easy to visualize. Make sure to disable it or hit "Reset Cache" if you're playing with the POPNet parameters.

Hope this helps

particles_sinwave.hip

##### Share on other sites

wow, amazing - i'll need some time to digest this.

It's interesting how one can be intuitively aware of math behind the problem, but completely helpless when it comes to implementing. It may be because in those days when I learnt calculus we didn't use computers. Then I spent years on pixel f***ing using computers, forgetting the math. Now it's time to dig a canal between those brain chambers and click them together.

##### Share on other sites

wow, amazing - i'll need some time to digest this.

It's interesting how one can be intuitively aware of math behind the problem, but completely helpless when it comes to implementing. It may be because in those days when I learnt calculus we didn't use computers. Then I spent years on pixel f***ing using computers, forgetting the math. Now it's time to dig a canal between those brain chambers and click them together.

You're welcome. Yeah, ultimately math is your friend when doing effects (and shader writing, and plugin writing, etc.), as painful as it might seem at first. It's really worth learning how to use a CAS (Computer Algebra System), so you don't have to remember the nuts and bolts of things like differentation, integration, etc., just the concepts of what it means, how to apply it, etc. There's a (long) list here:

Computer Algebra Systems

One that you can use straight from the web is Sage.

For example, the documentation for diff, the differentiation operator is here.

Speaking of painful math, I realized I made a mistake in the above formulas. You probably don't want y as a function of x, rather just a function of t as well, and then x should have it's own speed parameter for how quickly it moves along the x- (or abitrary) axis. So:

x = S * t

y = A * sin(F * t)

dx/dt = S

dy/dt = A*F*cos(F*t)

Also, on the Trail SOP, changing the result type to "Connect as Polygons" and un-checking "Close Rows" will make lines out of each particle's path, which is probably a better visualization.

Attached is a fixed result. I also added attributes for per-particle frequency, amplitude and timeoffset in the POP network, which is really straightforward.

particles_sinwave.hip

Edited by johner
##### Share on other sites

excellent!

I'll dive into the math right now, but before I do that just one practical workflow question:

why is that now particle motion is completely immune to anything else e.g. gravity?

It seems like VOP POP discards anything before or after itself (except initial velocity from source).

My intention was to use this "Sine Wave VOP POP" as a modifier to enhance already complicated particle animation, just like any other OP in a chain.

Any hint on how can I adapt your VOP to behave friendly to other POPs?

sorry for so many questions!

thanks!

EDIT:

The Velocity and Position POP override any existing velocity or position attributes. POPs is a simulation, so it takes into account every node first, then performs the simulation. Generally, the order in which the POPs are in does not matter (unless its a split/soucre/collect POP...etc). One way to solve your problem would be keep \$VX, \$VY, \$VZ in your Velocity POP and add whatever your are currently using so it would be something like this \$VX + 1, \$VY + 1, \$VZ + 0. This allows it to take into account any additional or existing velocity.

now I got it!

Edited by watchman
##### Share on other sites

excellent!

I'll dive into the math right now, but before I do that just one practical workflow question:

why is that now particle motion is completely immune to anything else e.g. gravity?

It seems like VOP POP discards anything before or after itself (except initial velocity from source).

My intention was to use this "Sine Wave VOP POP" as a modifier to enhance already complicated particle animation, just like any other OP in a chain.

Any hint on how can I adapt your VOP to behave friendly to other POPs?

sorry for so many questions!

thanks!

EDIT:

now I got it!

Right, the example above is setting the velocity directly, so it's not going to obey other forces. Can you do the sin wave positioning as a "sweetener" after the POP network has run, or do you need the particle motion as part of the sim itself? To really do things correctly, you would probably want to add to the acceleration on the particles using the second derivative, so the SinWave POP would be just another force. The problem here is orientation: you end up in kind of a feedback loop. If you're using the current velocity to orient your sin-wave offset, then next frame that velocity will already have the sinwave offset baked in, so you're sort of applying the effect twice. You need an "un-sinwaved" velocity vector to use as your orientation frame before offseting it with the sin-wave, if that makes sense. While this is doable in POPs, it starts getting pretty tricky.

On the other hand, it's simple in SOPs. You just run your POP network without any sinwave stuff, then use the velocity on the particles at the SOP level as your orientation. I've attached an example. Also here (more calculus), I'm using CHOPs to fetch the velocities of the particles, take the velocity magnitude (speed), the use the Area chop to integrate it and get total distance travelled by each particle for a given frame. If you use that instead of the age of the particles you get a consistent wave motion to the particles as they move along, independent of their current speed. You can still use age of course.

particles_sinwave_SOPs.hip

##### Share on other sites

man, I like "sweetener" SOP solution and I really admire your knowledge and willingness to help!

maybe I'll try to solve the equation in POPs just as an exercise, but this works.

Houdini is brilliant, when you hit the wall in one direction, just regroup and find another way on another front.

thank you very much!

##### Share on other sites

man, I like "sweetener" SOP solution and I really admire your knowledge and willingness to help!

maybe I'll try to solve the equation in POPs just as an exercise, but this works.

Houdini is brilliant, when you hit the wall in one direction, just regroup and find another way on another front.

thank you very much!

Sure thing. Of course I realized I made another mistake. Turns out using the Area CHOP to calculate distance traveled doesn't work so well if all your particles are not emitted at frame 0, which they're usually not. Plus CHOPs is kinda overkill for this. I think you're better off just using simple numerical integration of the speed at each POP cook. All that means is create a distance attribute in POPs, then each frame add to it the current velocity magnitude times the "timeinc" variable. This is just approximating the integral with a sum. A VOP POP to do it would look like the attached:

##### Share on other sites

so the purpose of AreaCHOP is to make wave's period constant in space and independent of particle velocity.

distance=velocity*time ( so if velocity is not linear, the distance is not linear as opposed to time), hence cosine function is using non-linear input for calculating period and that is compensating for particles ever increasing velocity - so the wave looks evenly distributed along the particle's path.

Although, in my case it looks even better without area calculus (it feels natural to have longer wave period if you're moving faster), for sake of learning VEX I want to implement distance from POP suggestion, but despite all common sense I can't make it working. I must be missing some basic essential knowledge of Houdini.

here's the non-working file:

particles_sinwave_SOPs_calculate_distance1.hipnc

(I guess my problem is how to make an attribute to accumulate itself every time step?

when I add the new/calculated value to the old distance attribute (which is 0 by default) I'm in fact adding to 0, so it's same as skipping add operator completely, which is wrong I think. Actually in that VOP POP I tried to output distance directly from multiply (skipping add) and got exactly the same result.)

Edited by watchman
##### Share on other sites

so the purpose of AreaCHOP is to make wave's period constant in space and independent of particle velocity.

distance=velocity*time ( so if velocity is not linear, the distance is not linear as opposed to time), hence cosine function is using non-linear input for calculating period and that is compensating for particles ever increasing velocity - so the wave looks evenly distributed along the particle's path.

Exactly.

Although, in my case it looks even better without area calculus (it feels natural to have longer wave period if you're moving faster), for sake of learning VEX I want to implement distance from POP suggestion, but despite all common sense I can't make it working. I must be missing some basic essential knowledge of Houdini.

(I guess my problem is how to make an attribute to accumulate itself every time step?

when I add the new/calculated value to the old distance attribute (which is 0 by default) I'm in fact adding to 0, so it's same as skipping add operator completely, which is wrong I think. Actually in that VOP POP I tried to output distance directly from multiply (skipping add) and got exactly the same result.)

You were very close. You had an AttributeCreate POP in there to create the Distance attribute, but you've got no group set and "Write Values" checked. What that means is that AttributeValue POP is setting the value of the distance attribute for all particles to 0 each time the POP cooks. To fix this, do any one of the following:

1) put "birth" into the group parameter for the AttributeCreate POP

2) un-check the Write Value param - that way it only creates the attribute if it's not there; it won't set the value back to 0 each frame

3) get rid of it altogether - the exported "distance" Parameter VOP will automatically create the distance attribute in your VOP POP if it doesn't exist

Also, your "Frequency" setting of 40 for your WaveVOP is way too high. You're just seeing aliasing here, since the sample rate (frame rate) is way too low for a sine wave with frequency that high. I'd set it closer to the 1-2 range.

As an aside, if you're trying to get really high frequency waves out of this (ie. frequency higher than the Nyquist limit imposed by the frame rate), you're going to have to get tricky if you want to use the Trail SOP since it only likes to cook at integer frames with input from POPs. You can't just crank up the oversampling on the POP net overcome those limitations in your wave frequency.

But if all you're doing is rendering the POPs output sent through the WaveVOP directly, if you need really high frequency waves, you should be able to crank up the oversampling on the POP network, crank up the "Geo Time Samples" on the Mantra ROP, and use Deformation Blur. Then your frequency is limited by the number of subsamples per frame (Geo Time Samples parameter).

##### Share on other sites

As an aside, if you're trying to get really high frequency waves out of this (ie. frequency higher than the Nyquist limit imposed by the frame rate), you're going to have to get tricky if you want to use the Trail SOP since it only likes to cook at integer frames with input from POPs. You can't just crank up the oversampling on the POP net overcome those limitations in your wave frequency.

Thinking about this more, this is wrong, at least in this special case. The trick is to feed the output of the POP net into the Trail SOP first, then use a Resample SOP to increase the resolution of the resulting lines (and interpolate the distance attribute), then apply the Wave VOPSOP. So you get around the frame rate aliasing by increasing the spatial frequency.

Dunno why you might want to do this, but attached is an example anyway.

particles_highfreq.hipnc

##### Share on other sites

Thinking about this more, this is wrong, at least in this special case. The trick is to feed the output of the POP net into the Trail SOP first, then use a Resample SOP to increase the resolution of the resulting lines (and interpolate the distance attribute), then apply the Wave VOPSOP. So you get around the frame rate aliasing by increasing the spatial frequency.

Ha, I did have that "jaggy" looking trails when I was attempting to fix the distance attribute, but didn't realize that was the sampling problem. Thanks for foreseeing my future problems before I even face them.

Btw, why does the Sine/Wave starts losing its "up" vector (correct orientation of " sine's height") if velocity of particles is bellow a certain threshold (i.e Source Variance = 1,1,1 and Gravity=0,-1,0) in your file? Is it sampling issue again?

3) get rid of it altogether - the exported "distance" Parameter VOP will automatically create the distance attribute in your VOP POP if it doesn't exist

So it is not necessary to declare a variable before VOP. That's great news for us non-programmers.

Purpose of "birth" group?

1) put "birth" into the group parameter for the AttributeCreate POP

what is the difference between "birth" group and "all" particles? If "birth" group is identity to "all particles born" why is that necessary to use it?

food_traffic_retreat_shot.mov - this is the actual effect that I'm working on. I'm pretty happy with the overall movement, thanks to your help.

However it's impossible to add trail to these particles because their IDs are messed. I have used source's IDs to shuffle through attractors, so attractors can be switched around randomly, but this is wrong approach definitely, as someone said before - if there's no motion blur, you're doing something wrong.

But it is probably the topic for some other thread.

anyways here's the hip file if someone wants to look around:

food_traffic_retreat_shot.hipnc

thank you very much for your help

##### Share on other sites

However it's impossible to add trail to these particles because their IDs are messed. I have used source's IDs to shuffle through attractors, so attractors can be switched around randomly, but this is wrong approach definitely, as someone said before - if there's no motion blur, you're doing something wrong.

But it is probably the topic for some other thread.

Actually, it's nothing to do with particle IDs or with my shuffling of Attractors. Even with the simplest source with limited particle life (1 sec), Trail SOP is not producing consistent polygons. So after Trail SOP (Preserve Original Type) I appended Add SOP, then Resample SOP before Sine/Wave VOP to get nice sampling beyond Nyquist limit imposed by the frame rate. Here's the result:

I still have to see how motion blur is behaving in this case.

##### Share on other sites

sorry for gibbering in previous posts...

my last sine-wave related question would be how to (in SOPs) orient Velocity Vector to be aligned with new Position?

tried Align VOP, Orient VOP...I want velocities to be affected by Sine Wave VOP SOP, so I can render correct motion blur.

thanks

P.S. visually, my problem reminds me to Orient Normals To Follow Curve, but don't know how to solve it in VOPs.

I suppose I should:

1. either do direct matrix transform on velocity vector

2. or ask for point Position in next time increment and then aim the velocity vector to that position

but my VEX knowledge is so low...

Edited by watchman
##### Share on other sites

Ha, I did have that "jaggy" looking trails when I was attempting to fix the distance attribute, but didn't realize that was the sampling problem. Thanks for foreseeing my future problems before I even face them.

Btw, why does the Sine/Wave starts losing its "up" vector (correct orientation of " sine's height") if velocity of particles is bellow a certain threshold (i.e Source Variance = 1,1,1 and Gravity=0,-1,0) in your file? Is it sampling issue again?

No, this is probably just due to the fact that we're using the velocity vector to get our orientation (using the LookAtVOP). If that gets close enough to zero there's nothing to orient by. In that case you can use a default value, cache the last good value, something like that. As long as the particles are moving around you shouldn't run into it too much.

So it is not necessary to declare a variable before VOP. That's great news for us non-programmers.

Purpose of "birth" group?

what is the difference between "birth" group and "all" particles? If "birth" group is identity to "all particles born" why is that necessary to use it?

Yeah, it's nice that exporting a parameter creates the attributes. It might not be a bad idea to have an Attribute POP in there if only to remind everyone that that attribute exists. The Help on the Attribute POP explains what turning off "Write Value" does, which might be the right thing to do in that case.

As for the "birth" group, I'm just creating that in the Location POP. It's a pretty common workflow to put all just-emitted particles into a group, then some POPs below will act only on those particles to initialize some attributes, store initial position, what have you. So if you have an Attribute POP that has the name of that group (in my example's case, "birth") in the Source Group field, it will only apply to the the new particles. The existing particles will be ignored.

this is the actual effect that I'm working on. I'm pretty happy with the overall movement, thanks to your help.

However it's impossible to add trail to these particles because their IDs are messed. I have used source's IDs to shuffle through attractors, so attractors can be switched around randomly, but this is wrong approach definitely, as someone said before - if there's no motion blur, you're doing something wrong.

But it is probably the topic for some other thread.

Ha! Very cool! Nice to see it in action.

sorry for gibbering in previous posts...

my last sine-wave related question would be how to (in SOPs) orient Velocity Vector to be aligned with new Position?

tried Align VOP, Orient VOP...I want velocities to be affected by Sine Wave VOP SOP, so I can render correct motion blur.

thanks

P.S. visually, my problem reminds me to Orient Normals To Follow Curve, but don't know how to solve it in VOPs.

I suppose I should:

1. either do direct matrix transform on velocity vector

2. or ask for point Position in next time increment and then aim the velocity vector to that position

but my VEX knowledge is so low...

I vote for number 1. We're back to the calculus from early in the thread. Since you know exactly what you're adding to the position parameter, you can calculate its first derivative (velocity), transform it by the same rotation matrix you're using for position, and add to the incoming velocity vector. See messy network attached and hip file.

Two points: the default "from" parameter in the LookAt VOP is not (0,0,0) like I thought, so make sure it is in your VOP, or do what I should have done in the first place and put a constant zero vector in there.

Also, I would think about using sine for your position like I have here, rather than cosine. The reason is that sin(0) is 0, while cos(0) is 1, so at time 0 you're getting an offset immediately if you're using cos. You may want that. The setup I have here uses sin for the position, so:

y = A * sin(F * t)

dy/dt = A * F * cos(F * t)

if you do it the other way, make sure to negate the amplitude in the derivative:

y = A * cos(F * t)

dy/dt = -A * F * sin(F * t)

particles_with_v.hipnc

##### Share on other sites

I vote for number 1. We're back to the calculus from early in the thread. Since you know exactly what you're adding to the position parameter, you can calculate its first derivative (velocity), transform it by the same rotation matrix you're using for position, and add to the incoming velocity vector. See messy network attached and hip file.

everything' perfect, thank you very much for your explanation, just one calculus detail is still bugging me:

```sin_position = vop_sin([b]freq[/b]*age);
product = sin_position * wave_amplitude;

cos_velocity = vop_cos([b]freq[/b]*age);
product2 = [b]freq[/b] * cos_velocity * wave_amplitude;
```

- why the frequency parameter has to be plugged twice when calculating velocity, as opposed to being plugged only once when calculating position?

##### Share on other sites

everything' perfect, thank you very much for your explanation, just one calculus detail is still bugging me:

```sin_position = vop_sin([b]freq[/b]*age);
product = sin_position * wave_amplitude;

cos_velocity = vop_cos([b]freq[/b]*age);
product2 = [b]freq[/b] * cos_velocity * wave_amplitude;
```

- why the frequency parameter has to be plugged twice when calculating velocity, as opposed to being plugged only once when calculating position?

Google "chain rule", or see here. In particular part of example II is closest to what we're dealing with.

##### Share on other sites

johner, thanks for all the explanations and links in this thread. That was a whole little short course in VOPs and calculus. :notworthy:

however, again

In order to now use "distance" parameter AND sine/wave on velocity I had to post-multiply cosine function with some - you will be laughing now - magical constant value (in my case it was approx. 0.93) in order to correctly align velocity to sine/wave. It's working with this little hack, but I'm sure there's a proper way to do it.

I noticed that instead of multiplying by "magic number" I can multiply it by "distance" itself and get closer to proper alignment, but not too close.

Also I noticed, that sometimes when the scene is not too small everything is working just fine if I dial in the right frequency by pure gamble.

## Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×