Difference between revisions of "HoudiniChops"

From cgwiki
Line 217: Line 217:
Download scene: [[:File:rbd_post_filter.hip]]
Download scene: [[:File:rbd_post_filter.hip]]<br>
Download scene for 16.5: [[:File:rbd_post_filter_16_5.hipnc]]
Download scene for 16.5: [[:File:rbd_post_filter_16_5.hipnc]]

Revision as of 23:36, 14 November 2017

Houdini Chops

Chops are weird. Its the scary corner of Houdini no-one talks about, the documentation is sparse, and artists who understand it walk around in a smug world of their own. Bastards.

Every 6 months or so I'll have a stab at it, and while I could follow along with tutorials, it never clicked in the way sops clicked or even dops, and dops are weird.

Well, with the advent of the channel wrangle and some help from discord (especially Henry Dean, who in one video did more to advance chops knowledge than 10 years of prior work), I think I've had that click moment, so I'm hastily writing it down before it fades away.

So what the hell is chops?

The standard analogy is 'audio processing', but there's a few other things chops does, handy to be aware of them all.

  1. Chops is a digital audio processor. Load in wav files, midi files, filter them, generate triggers to spawn particles or drive animation, all that is well covered.
  2. Chops is a rigging toolkit. Feels like this will get more attention going forward, but already a lot of the rigging and constraint tools are done via chops. Dynamic parenting, Euler rotation filtering and cleaning, aim at constraints, IK handles, all that sort of stuff is chops.
  3. Chops is a way to model and sculpt animation independent of time. If you could convert all your channel curves into real polylines in a 3d viewport, mush them around, distort, combine etc, then send them back as animation data, that's what chops lets you do. Here's a laboured gif analogy:

Here's a bouncing ball:

Chops ball bounce.gif

In other 3d packages you can display the motion path (here I've faked it in Houdini):

Chops ball path show.gif

Now imagine that motion path was a regular curve in sops. Ideally you would be able to model and distort the curve, and have the animation update. You wouldn't be limited to whatever tools the keyframe editor allows, you'd bring the whole sops toolkit to your animation workflow. What you've also done is stop looking at time through the timeline, you can now manipulate all moments of the animation at once. All the animation is splayed out in a human readable format.

This is what chops does, not just for translation like the sphere above, but for all animatable parameters, and not just for a single position, but for all the point animation on a complex shape, or all the joints in a rig, or all the things that are changing over time (within reason!).

How to setup a chopnet for sops

I hope you agree that all the fun stuff happens in sops. So its surprising that you have to jump several hoops to get sops to talk to chops. The steps are:

  1. Define a node where you want chops to read from, usually a null with an obvious name
  2. Append a channel node to read the data back from chops, set its method to 'animated'. This will be in an error state until you give it a chopnet and a chop out node to read from.
  3. Create the chopnet, dive inside
  4. Create a geo node to read from the null from earlier, set its method to 'animated' too
  5. Append a null to be the export location, name it nicely
  6. Go up a level, select the channel sop, set the path to the chop null (eg '../chopnet1/OUT') set its mode to animation
  7. Have a drink

Here's my best speed run. Ready.... GO:

Chops setup.gif

See? Simple!

Deciphering the motionfx graph

So... what have we done? A geo chop reads @P by default from sops, but by default only on the current frame. Switching the method to animated will read the full timeline animation. If you create a motion fx pane, you'll see all the animation data graphed out, much like an animation view. It'll probably look super busy too, if you hover over the pane and tap L on your keyboard, it'll hide the labels and make things run a little faster.

I've taken a line with 10 points, and in a wrangle set it to wave back and forth along x with a simple @P.x = sin(@Time);

Here's how that looks next it its motion fx graph:

Line graph unaltered.gif

I've left the labels on here, you can see the sine wave motion on the blue, well, sine wave. The 80s pink+purple lines are the y positions of each point; they don't change, so they all stay as flat lines. What you can't see is tz; all the points share the same tz value, 0, so they're all hidden at the 0 point.

What's all this tx/ty/tz stuff? Look at the geometry chop, you can see it has an attribute scope, P, and a rename scope, tx ty tz. Unsurprisingly, this takes each component of @P, and renames them as tx ty tz. I suspect this stems from a few things; chops is old and does its own thing compared to the rest of Houdini. Also I think its original purpose was to deal with object level transforms, where those keyframeable parameters are called tx ty tz, rx ry rz, sx sy sz.

Note that on the channel sop, it does the reverse, reads in tx ty tz, and converts them back into P.

The spring chop

So say we limited the sine motion to a single pulse by clamping time (and speeding it up so its more interesting) : @P.x = sin(clamp(@Time,0,1.57)*4);

Inside the chopnet we can add a spring chop, and get some cool stuff (excuse the size of this gif, but its handy to see all the things):

Line add spring.gif

It's important to remember that that isn't a simulation; it's better to think of it as a deformer, for motion. Note that I can change the properties like mass, decay etc, and the graph updates in realtime. There's no run up, no recaching, no sim. That's kind of cool. What else can we do?

Stagger channels with a wrangle

Spring boxes.gif

Download scene: File:chops_example.hipnc

Now that we have the channel wrangle in H16, we can do some fun stuff.

Each line of animated data is called a channel, within chops they're kind of like a packed prim for animation; they're an atomic unit, they have attributes, and have sub-data (kinda like unpacking a packed prim, don't think about this analogy too much).

In a wrangle context, the important attribute names are V for a value at a given time, C for the channel number, and a few other helpers like CN for the channel name, They're all listed here: http://www.sidefx.com/docs/houdini/nodes/chop/channelwrangle#globals

There's not a lot of chops specific vex functions, but the ones they have are useful. A handy one pointed out by Heny Dean is chinput(), which is sort of like a point() call, it lets you access other channels and other values of those channels.

So here, lets take this boring back and forth motion, and do something cool; stagger each point based on its channel id. Insert a channel wrangle after the geometry chop with this code:

V = chinput(0, C, I + C*ch('offset'));

Click the button to make a slider, slide it while watching the motion fx window (and the viewport):

Line add splay.gif


I remember doing lots of keyframe anim like this in maya, keying many objects to move in lockstep, then in a dopesheet window offsetting each object by a few frames to get a sexy staggered motion. And here's houdini doing it procedurally. Oh the time I've wasted....

More anim stagger

Chops anim splay more.gif

Download scene: File:chops_repeat_and_splay.hipnc

The top row is a polyextrude being driven by a @zscale attribute. In a wrangle I've keyframed that attribute, because its the same for all prims, they all extrude with the same animation.

The middle row is the same, but processed through chops before going to the polyextrude. Similar to the previous example, I stagger the animation using a wrangle, and apply an echo to repeat the animation with a delay and decay.

The bottom row is also the same, but showing how to generate a new animation within chops, then copy+stagger to all the prims. A trigger chop is used to define a new waveform, then 2 maths nodes are used to copy that waveform to the incoming channels. The first just multiplies all the incoming channels by zero, effectively resetting them. The second math chop just adds the newly reset channels to the trigger channel, so each channel is now a copy of the trigger. There's probably more elegant ways to do this, but the advantage with this method is that it'll work no matter how many incoming pieces of geometry there are. I use the same chop wrangle trick to splay/stagger the animation.

Stagger and offset bones

Chops bones final.gif

Download scene: File:bones_chops.hip

Same idea as above, but the end result is more fun, and covers how to bring object level parameters into chops, plus a few nice H16 UI tricks.

First, drawing a chain of bones. Make sure you're in /obj, hit tab in the viewport, choose 'bones' (not bone). Click the start position, move to the end position, but before you click, scroll the mousewheel up. Hey hey, instant bone chain. Click to set the end, hit enter to finish the tool.

Bones chain create.gif

Now to create a chops network for these bones. Initially we'll just affect the rotate-y channel of these bones, so select them all in the network editor, bring up the parameter pane, right-click on the rotate-y channel, and choose 'motionfx -> wave'. This will create a chopnet, pull all the rotate-y channels in, apply a wave channel effect, and link the result back to these channels. Pretty slick.

Motionfx create.gif

Houdini will have also spawned a floating parameter pane for the wave chop, close that for now, and look inside the chopnet. It's pretty simple; the channel chop pulls in the animated channel names, the wave chop generates a wave, the math chop adds them together, and because it has the export flag set, sends the result back to our joints. Set the amplitude on the wave chop to 16, admire your nice wavey motion.

Having a closer look at the channel chop though, you can see its a multiparm that references each channel by name. It's a bit unwieldy, and doesn't support wildcards. We can do better than this. Drop down a fetch chop, set the path to a wildcard that'll get all our joints: '/obj/chain_bone*', and set the channel name to ry. Swap the fetch for the channel chop and... an error?

The fetch chop can be temperamental for reasons we'll cover soon, to fix this click the light-orange export flag on the math chop to disable it, then enable again, should now work.

Chops fetch setup.gif

Mmm, much more procedural, sweet. Now like the previous example, lets create a chop wrangle to stagger the timing per joint, and drive that from a slider. Append a wrangle, move the export flag to it, enter the following code, click the 'create sliders' button.

V = chinput(0, C, I + C*ch('offset'));

Chops bones wrangle01.gif

Notice though that each time I move the slider, I have to do the same disable/enable export flag? That's pretty annoying. Luckily Henry Dean found a great workaround for this. He explains here that its due to a cyclic dependency bug in the fetch rop, but you can bypass this by putting down a constant chop after the fetch, hit the 'snapshot input' button on the snap tab, then disconnect the fetch chop. If you look at the 0 tab, you can see that it copies the incoming channels, but now they're just static names, removing the dependency issues.

Now you can let the playbar run, and slider the sliders to see the lag run in realtime, which is great fun.

Chops constant workaround.gif

With this setup, its easy to make silly changes. Eg, say we wanted to animate the rotate-x channels too, and have them use a different offset and amplitude. First we have to import those channels, so reconnect the fetch chop, and change the channel name from 'ry' to 'r[xy]'. Snapshot the new names and disconnect the fetch rop again. Now the rx channels are available to chops.

To update the wrangle, I add an if statement that looks at the channel name; if it ends in 'x' (ie, its an rx channel), then use a different chinput call using different sliders:

float oy, ox, ay, ax;
oy = ch('offset_y');
ox = ch('offset_x');
ay = ch('amp_y');
ax = ch('amp_x');

V = chinput(0,C,I-C*oy)*ay;
if (CN[-1]=='x') {
   V = chinput(0,C,I-C*ox)*ax;

The gif at the start of this section shows playing around with those sliders, pretty cool.

Should point out that I still don't really know how to skin geo to these bones, nor do I have much of an interest in learning right now. Maybe one day...

Dynamics driven by chops

Chops dops.gif

Download scene: File:chops_drive_dops.hipnc

Download mp3: File:beat.mp3

A more stereotypical example of what people think of for chops. A mp3 beat is driving the vertical motion of a ball. The ball has its motion transferred to a grid, which then drives a ripple solve. The ripple solve is in turn used to drive a particle sim. To make the gif make sense, I also drive a line using the same mp3 like an oscilloscope, but unlike an example elsewhere on this site, this is using a channel wrangle.

Nothing overly complex here, but when getting into this sort of stuff it can be hard to know where to start, hopefully this give some clues.

Using the beat chop

Beat chop sm.gif

The beat chop works like a metronome, and like most audio editors you can tap a beat and it'll then guess what beats-per-minute you've tapped. It expects 2 input channels, 'listen' and 'tap'. If listen is non-zero, it tries to work out the bpm from pulses in the tap channel.

You can generate those channels with a keyboard chop, holding down a key to enable listen mode, and tapping another key to set the beat. To allow the keyboard chop (and other input chops) to detect keyboard presses, you press the scroll-lock key. The time indicator will turn orange to show that chops is now in detect-inputs mode. So:

  1. keyboard chop, name first channel 'listen', second 'tap', leave the keys as 1 and 2 if you want
  2. connect that to a beat chop
  3. hit scroll lock, see the frame indicator turn orange (I had to use a virtual keyboard, no scrolllock on my mini laptop keyboard)
  4. hold down 1 to enable listen, tap 2 to set the beat

I'm using an on-screen keyboard tool partially so you can see what I'm doing, but mainly because the little surface pro 4 I'm using doesn't have a scroll-lock key. :)

Glitch effects

Chop glitch.gif

Download scene: File:chop_glitch.hip

Super cool tip from the incredibly helpful and friendly Henry Foster aka Toadstorm. He mentioned the cool banner on his website was done with Chops, dropped a few hints in a discord chat, was enough to get me to work this out.

The core idea is simple; images probably shouldn't have audio filters applied to them, so if you go and do that, fun glitch stuff happens. A resample starts to offset the alignment of the colours from where they should be, leading to interesting Amiga style loading screens, and a lag chop really breaks things, leading to more interesting effects. There's a wealth of things you could do here, lots to explore.

The actual mechanics of getting the colour info in and out of chops is fairly simple. This is a grid where I've set Cd with an 'attribute from map' sop, then I read Cd into chops, and pull it back out again with a channel sop like the geometry examples above.

The only tricky trick is to make sure its working on Cd, not P. To do this I tell it what the attribute is (Cd, obviously), and what 3 channels to map that to in chops, I've gone with cdr, cdg, cdb. Set the same names on the channel sop, and you're good to go.

RBD filter high frequency noise

Rbd chops filter.gif

Download scene: File:rbd_post_filter.hip
Download scene for 16.5: File:rbd_post_filter_16_5.hipnc

Short version: cracktransform -> chops -> foreach chop -> construct t* r* s* channels -> transform chop -> filter-> eulertoquaternion(radians( filtered_rotations ) )

Often RBD sims will have high frequency pops and jitters that can be a pain to remove. I knew chops should be capable of filtering that noise out, and its relatively easy to do for position, but rotation, specifically @orient, took a bit of effort.

My first attempt was dumb, read in @orient as a vector4 (so I set the geo chop to read @orient, and create channels rx ry rz rw), filter, then reapply. It fixed the most jittery RBD objects, but others that had been calm would flip erratically. On closer inspection it looked like euler flipping.

I knew chops should be able to fix euler flipping, so I started investigating that. From reading around, it seemed to be as simple as applying a transform chop. I did that and... nothing. Further reading said that it expected euler values, which makes sense.

So I used vex cracktransform() to convert @orient to euler, store it in @r as a vector attribute, read that into chops, and.... still nothing. Yet more reading, head scratching, it seems that the transform sop is designed to work with /obj level transforms, not arbitrary rotations on points. As such, it expects a full set of translate/rotate/scale channels, so tx ty tz, rx ry rz, sx sy sz.

I made some dummy channels for translate and scale, merged and... STILL NOTHING. Hmm. Yet more head scratching, suddenly realised that it expects literally those names. The geometry chop appends a number suffix to each channel that corresponds to each point, that confused the transform chop.

Even more head scratching, and I found the for-each chop. I could process each point by stripping the name suffix, create the extra required channels, feed that to the transform chop. Finally it would now apply the euler filter, and I could see clean channels without discontinuities. Hooray!

Except... it went crazy when applied back to the points. It took Nick Taylor and Henry Forster to point out my stupidity; I was using the eulertoquaternion function to convert back to orient, it expects radians, I was using degrees. Idiot. A quick cast to radians, and finally convert @orient back to the intrinsic transform of the packed geo, and it all works. Fiddly, but should be very useful!

Henry Dean pointed out that 16.5 has a euler filter chop that works on just rotations. Hooray! Will test that when I get a chance...

Careful now

Before you go and chop all the things:

  • Geometry chops don't scale well. Makes sense really; if you have a 50x50 grid over 240 frames, then that's 50 * 50 * 3 channels it has to generate ( 7500 ), times 240 frames = 1,800,000 bits of data. Remember if you want to access the entire timeline of animation at once and manipulate it, then all that has to be available, then all of _that_ has to be modified as you slide values on the spring chop. Made worse because...
  • Chops are single threaded, or were last I checked.
  • Chops can lock Houdini super easy. All those channels, all that data, put down some fancy chop, you'll pay for it.
  • Chops are disliked and feared by most Houdini artists. Remember I said chops savvy folk get all smug? Part of that is knowing if they have to share their scenes with others, there'll be tears. Don't be a dick, keep the chops stuff to only when you really need it.

Additional resources