In addition, I am attaching the rig I've built [www.sidefx.com] for anyone to take a look at if they wish. This is a basic biped with IK/FK switching, reverse feet, and a custom CHOP setup for the neck. There are a few hacks and it's not highly debugged or well tuned for aesthetics, but it may be a useful resource to get started on your own research. Any tips or suggestions to improve it would be highly appreciated as well.
This post is fairly long because I wanted to get everything written down while it was fresh in my mind. If there is interest I would consider a separate technically focused writeup explaining my understanding of Transform CHOPs and constraints, maybe in the form of a video tutorial.
I found the process of constructing the rig hierarchy highly intuitive and pleasant. Laying out a rig in the node graph makes a pleasing parallel with anatomy of your character, and the construction plane plus the bones tool is a killer combo. On a technical level, Houdini rigging is extremely similar to how it's done in Maya. That knowledge transfers directly. Some parts take getting used to, like dropping nulls in your joint hierarchy to create translation offsets, but the overall process of setting up joints is quite nice, and the “bones vs joints” debate is really a non-issue as long as you're keeping your model inside of Houdini. Being able to rework your hierarchy so easily is a boon. I was quite happy using Houdini's Python API to help automate the process as well.
However, I've run into a number of serious issues as well. Of course there were everyday papercuts that come along with rigging, though they seem to be more prevalent here than in other areas of Houdini. One big letdown for me was CHOPs. It's not that I dislike them, but after watching Henry Dean's tutorial, I thought that CHOPs would be one of the greatest, most powerful aspects of the Houdini rigging process. The reality was as soon as I wanted to invent something new, or take more into CHOPs than the basic builtin constraints, I found myself trudging through unexplored territory with little documentation and lots of gotchas.
They are also one of the primary culprits of the big, bad, looming issue with Houdini rigging: performance. The interactivity of the rig is poor enough on my machine that barring some new method to dramatically increase the speed, I do not plan to do more character rigging in Houdini for the time being.
This is basically a scratchpad for RFEs I might submit to SESI.
- It would be nice to provide an option on FBX to translate translated the hierarchy to a natural Houdini style on import instead of rigidly preserving the original bones. Even if the translation is lossy, or animation had to be discarded, it would be a bit easier to get started.
- I copied an existing Bone Capture from my FBX onto the rig. Working with weights/capture data in Houdini seems like a bit more work than necessary.
- Using Houdini's mirror tool puts a bunch of negative scales on the mirrored nodes instead of rebuilding the rotations. Negative scales worry me, and the CHOPs on the right hand side are more prone to desync and errors. By “desync” I mean various effects where saving & reloading will alter your rig evaluation. See 1:24:45 [vimeo.com] for an example. Disconnect/reconnect doesn't always fix things like it does there.
- The Capture Correct node could use a few touchup options like “add/remove joint”, “rename joint,” etc. so you don't have to drop into VEX for simple tasks.
- How does one save/load capture weights to an .ocapt file?
- The Houdini idiom of constructing a rig inside an HDA and promoting the appropriate transform parameters to the upper level means it's no longer possible to reset your xform by ctrl+middle click.
- Selecting and moving around controllers is still unergonomic. The pose tool seems like a good start, but it's not “instantly make Houdini even faster than Blender,” which should be the aspiration for this kind of tool. Blender is IMO the fastest DCC for viewport manipulation.
- Maybe Houdini's autorig package could include a Python panel GUI selector, or an API to build one.
- Python Hou. function names are a bit verbose. That's fine. It would be nice to extend the available searching and filtering functions to make manipulating the node graph easier.
- Why isn't hou.Node.type() comparable to a string? Why no hou.Node().setParm()?
- Transform wrangle syntax error line nos are off.
- Viewport subdivision slows Houdini down to a crawl - 2fps or so! That shouldn't be so costly!
The biggest pain point in the rig building process stems from CHOPs. Matt Estela describes them as “the scary corner of Houdini no-one talks about.” I don't think there's anything particularly frightening about the logic of CHOP networks, but what really got to me are the limitations and things that are either quirks or bugs. CHOPs are prone to falling out of sync with your scene and cause undue distress as you try to figure out why your network is broken. This is especially true for wrangle nodes and IK solvers.
I spent a while developing a distributed neck bend system to better understand the logic of Transform CHOPs, and to try to speed up the rig. What I wanted to do was constrain each neck bone to progressively tilt in the direction of the controller - essentially aim constraints with less than 100% weight - to get a progressive bend. The simplest method of doing this, writing an aim constraint network for each neck bone, was causing a performance hit. Next I tried writing a single CHOP network that would compute all the rotations at once. This seemed to be feasible, but the performance wasn't great, since I was simply exporting the channels at the end of the CHOP into the bone rotation channels. Finally, I was able to get the speed boost I was hoping for by plugging my CHOP network in as an IK solver. An IK solver just exports local rotation channels, so I was a bit surprised that this was faster than a CHOP export, but the performance logs seem to indicate it's the case.
Another area I struggled with when writing this was the structure of the network itself. Each node in a CHOP network causes a performance hit, so I wanted to minimize the number of nodes. Unfortunately, generating four sets of new local rotation channels from a single transform in a Channel Wrangle is awkward to do. You can't write channel data unless you're in one of the “Channels x Samples” iteration modes. The solution I found was to munge the data through a “Channel Attribute” instead: first you compute the rotations and stuff them into a chattr, then use a second wrangle to actually write the new data channels. I'd be happy for a cleaner solution. Or maybe there's something even faster than the IK node technique!
I don't mean for this post to be a CHOP hit piece. Transformation constraints are complex and subtle by nature, No DCC makes it easy, and there are always gotchas. My opinion is that CHOPs could easily be the best in class for writing rigging constraints. They are certainly more powerful and open to inspection and manipulation than in other software, once you get used to how they work. CHOPs are unusual for sure, but after this exercise I consider them easier to work with than DOPs - very linear with a clear data flow. Writing VEX formulas for your rig instead of building it up with “addSubtract” nodes is a absolute pleasure. But to improve rigging in Houdini, either CHOPs will need to be replaced with a new rigging/constraint context, or this corner of the software will need a dusting off.
The Elephant in the Room: Performance
When I shared my rigging experiment results on Discord, one of the first reactions I heard was the following:
I have talked to some people who say that Houdini is 10-20 times slower when it comes to comparable character rigs.
The performance discrepancy wasn't 20x, but I wish I'd been told about this before I started my research project, since right now it's a showstopper. This is a very basic game-quality character rig, and it's already unpleasantly choppy to use with a model pared down to the basics of ~6.2k polys, I get about 18fps when moving the COG, and 25-30fps moving the IK arms. A comparable measure in Maya, though with a more complex AdvancedSkeleton rig, is roughly 30fps for COG movement and 80+ on the limbs. That's well past the buttery smooth threshold, and the Maya rig hasn't been tweaked for speed.
A back of the envelope 2x performance diff is not the whole story. Let's take a more concrete look at the performance numbers. Please note this is on an older box with a 750ti. My home workstation has run into some hardware issues so I'm using my server instead. On a newer machine, I got around ~600ms of viewport evaluation for 2000ms of node evaluation.
The top item on that list, MAIN_GEO, is where the deform SOP is evaluated. The release notes of Houdini 17 claim improved rig performance by speeding up the bone deformer and viewport display. I haven't ported this rig back to 16.5 to do a serious comparison, but that result seems reasonably fast to me. The deformer takes up a little over 1/10 of the whole node graph evaluation. Sure I should chop up the rig and parent it to the bones for a speed boost, but even in an optimistic scenario where the geometry has no cost, an extra 15% FPS isn't going to get me to a smooth frame rate. Also, I don't need to go through that inconvenience to achieve smooth interaction in Maya! (I wonder: is the deform computed on the GPU? What about viewport subdiv?)
After that, the performance is attributed to long list of CHOPs for space switching, IK, and other constraints. It should come as no surprise that CHOPs and constraints are as costly in Houdini as Maya's constraint nodes, which are famous performance killers, since the two systems do nearly the same thing under the hood. However there are some additional performance surprises that aren't shown. When rigging the fingers, Michael Goldfarb uses parent constraints with an offset to make the the finger bones track the controllers. There's no need to store an offset, and evaluating 38 additional offset nodes + parent nodes causes a serious performance hit, so I set the finger joints to simply track the controllers in world space. Even after this optimization the fingers remain ~10-20% of the evaluation cost.
Another CHOP performance pain point: Constraint Blend nodes don't seem to correctly terminate execution of the node graph on upstream nodes with weight zero. Houdini seems to spend time evaluating nodes with zero weights on a Constraint Blend. I've attached a second performance log [imgur.com] with more details on the internal breakdown of these Parent Blend constraints. Hopefully this is just a bug.
After that, the performance monitor is simply a long list of nodes with no obvious culprits. But there's another important piece of data: the task manager. Probably the most striking thing I discovered is that Houdini seems to be limited to a single core while Maya uses several. That is, Houdini tops out at 8% on my 8700k, which is 1/12 threads, and 3% on the Xeon box I'm using at the moment. Maya is of course engineered around fast custom rig evaluation, [twitter.com] but as far as I can tell, there's no way to evaluate a rig in parallel in Houdini at all. Hou is a performance beast inside of SOPs, DOPs and VEX, but up at the OBJ level it carefully tiptoes through the graph on a single core. If that is the case, it's by far the most fundamental limitation of Houdini's rigging capabilities. Even a mindblowing pose tool and the best autorigger on the market won't make Houdini competitive for character animation if the performance stays as slow as I'm experiencing.
That fact is what causes me the most hesitation in investing more time in Houdini rigging. Sure I could implement those Python callbacks and chop the model up to get a usable rig for our gray lambert man. But what happens when the rig requires more complexity? How about rigged toes, doubling the performance cost of the fingers? What about wings, horns and a tail? What about a complex facial rig? What about props, or parenting an entire rig to another? What about componentized parts?
The Houdini workflow is very well suited for rigging and animation. I'm sure SESI sees it as a strategic opportunity as well given the flood of new tools for animation and rigging. However, if my understanding of the parallel evaluation situation is correct, making Houdini competitive will require deeper architectural innovation. Perhaps the solution is a new graph evaluator, tagging subnets to restrict them to parallel-friendly operation, or even a new rigging context altogether to merge OBJ and transform CHOPs. Or perhaps I'm doing something wrong and someone more experienced can tell me how to make it faster! I hope to see interactive rig performance in Houdini someday soon so I can make that “100%-H” dream happen.