Artist Shows Grooming Workflow for Curls, Afros, & Braids in Houdini
Piotr Zielinski talked to us about grooming workflows for different types of hairstyles: curls, afros, and braids, with Houdini, and the use of procedural techniques in the process.
Introduction
Hi, I am Piotr, a freelance Character Artist for games and cinematics, mainly focusing on Unreal Engine as a render environment. At the moment, I have been working on a long-term contract with Aaron Sims Creative for almost five years already, where I am responsible for modeling and preparing all kinds of assets for cinematic production in Unreal Engine 5, from characters to robots and props.
On the side, I run a small mentorship, where I am helping other artists to grow their skills and make sure they make fewer mistakes along the way. I also started working as a mentor for a think tank training center lately. Whenever I find some free time, I always make sure to improve my own skills as well, always trying new techniques and tools.
I also wanted to state that I am not a professional groomer (yet), so if anything I am saying here doesn't make sense, feel free to let me know.
While working on this project, I took part in a Houdini workshop with Luis Cadavid and spoke a lot with Artem Erikenov, who both helped me a lot with many aspects, and I want to give them credit for a lot of things I am explaining here.
Project Aim
The purpose of that project was to get more accustomed to Houdini, grooming workflows for different types of hairstyles, and how far it is possible to do procedurally with the least amount of actual grooming.
Skin
Regarding the skin and attribute painting, what I think is worth mentioning, and what was the biggest difference for me coming from XGen, is that everything in Houdini can be procedural. So, for example, when you paint an attribute, it might not be worth painting it with a nice smooth transition from the start, but instead paint it with a sharp line and then use the attribute blur node, so you can have more control over the effect.
Another one is that you don't have to paint the whole mask with one paint attribute, but instead you can compose it with a wrangle node, and what's even better is that using a fit01() function, you can make your attribute stronger, reduce it, or even invert it.
Curls
Guides
A curl hairstyle uses two levels of guides. The first one was groomed by hand and sparse enough, so I could control the overall flow and direction of the major clumps with ease. I would always start with placing only half of the head, then mirror that and break up the symmetry after, this way I have more control and can preview the whole hairstyle quicker.
In those curly hairs, I had a lot of issues with curls intersecting one another. What helped with it was adding a lot of thickness to the sculpted guides; this way, I could better preview how much distance I need to keep between them, so later the curls have more space.
Then, the second level of guides was generated and curled procedurally based on the initial guide placement.
Very important for the final look was using a custom density attribute that was adding some noise variation on how dense the guides' placement would be, and then adding a lot of density on the split to ensure good coverage and smaller clumps in this area.
For more control and variation, I split the guides into three groups using a guide mask and group nodes. Front one using the length ramp, then inverted it to get all the rest, and that one is split randomly in half using random masking from the guide mask node.
All the curls are made with a hairclump node and curling setting inside. For more variation, I was using amplitude and frequency attributes prepared before in an attribute wrangle. The idea is that, instead of one single value for amplitude and frequency for all the curls, I could use some randomisation in between set values. Those were all a bit different for the guide groups described above.
This was the most important part for the curls. Next, I was using multiple length nodes to add even more variation. I also added some controls to make certain parts of the groom shorter or longer, like the sides, back, and top area. That was only needed for being able to make some quick iterations on the shorter variations.
Hair
Having dense lvl2 guides prepared, setting the hair is straightforward. I started by putting them in place with a clump node and using my lvl2 guides as custom clump curves. Then I prepared some guide masks for simple variation adding, one for long, one for short, and one for random flyaways. Next, I continued by adding some frizz, then some subclumps, then more frizz and more smaller subclumps.
A cool trick is to reference a bigger clump size into the smaller one, so they always stay relative, no matter how you change the first. I chose to have smaller clumps be ⅓ of the medium size once. You do that by simply right clicking clump size text, then you copy the parameter, and you paste the relative reference to the other clump node.
After that it's some frizz and flyaways, nothing fancy, it requires a lot of back and forth in between all the modifier settings for the hair and guides setup. In the end, before exporting, I used a curve resample by density node from labs that allows me to optimize the strands a little for Unreal.
Afro
Guides
Afro guides also had two levels of guides. First was setting bending on the sides, on the back, and basic frizz. Second lvl guides were responsible for the amount of clumps in the future hairs. The density amount, combined with the density attribute, allows for getting either a lot of small clumps or a few very wide ones.
Hairs
To get the frizzy look that I liked, I had to use a lot of multiple frizz nodes, and proceeded with some base curls guided by lvl2 guides.
For optimization and to fill the space better, I had the final hairs combined from main clumps and some background filler noisy short afro hairs (filler previewed with red color).
Braids
Guides
For braids, I wanted to try Metahuman tools released for Houdini grooming some time ago by Epic. They helped a lot with both braid and cornrow setups. The only manual grooming was for the main guides that would later be used for two thick braids, all the smaller ones, and the cornrows.
What's great about the Metahuman groom braid node is that it can output tubes out of the created braids, which allows it to generate collision in between each strand; that's crucial for the braids looking nice and tight without much inner penetration of the strands.
Hairs
For braid hair, I started by taking the tubes output from mh braid node before, then I used mh tube fill that allowed me to fill all the tube space uniformly with hairs, so the coverage is good, also making sure it will fit the tight braid silhouette.
One of the issues I had with the Metahuman nodes is that the built-in rooter for hairs had a locked value on how much the root is spreading, and even the smallest allowed value was too wide for me, so I had to create a basic rooter by myself.
How it works is it selects a first point, then expands for a couple more points that allow it to control how far the root will reach, then removes all those points in between the end of selection and the root point. Then it jitter points a little for variation and projects them back to the surface. Finally, some resampling and a little smoothing.
In the same way i expanded and jittered tips. Next, I prepared a group for color variation. I wanted to have control over how it looks and not have random strands; for that, I placed a couple of primitives, detected points intersecting primitives, and a few strands, then expanded those points into full strands and converted that to a group for future use in Unreal.
Styling the hair was straightforward: a little clumping, slight frizz, and a random cut of a very small percentage of hair that I prepared in the beginning with a guide mask.
The last step was optimising the number of points for Unreal. For now, it has a hard-coded limit of 255 points per strand we can use; anything above that gets deleted, so all hairs, like braids, dreads, or afro are a bit problematic.
I used curve resample by density from labs, so I could specify a flat amount of points it can use, then with ramp area of a braid that would hold with fewer points than others, this way it moves some amount of points from the selected section to roots and tips so they held shape a bit better.
Rendering
In terms of rendering, there is nothing major, except for the hair cinematic blueprint from Argentum Studio. It is not a scheduled ad for them; it just works. I had two lighting setups, one for default lookdev and one for beauty shots. They consist of a couple of lights and a skylight that's important and fills all the otherwise dark shadows very nicely. On top of that hair blueprint to make it look even better. The settings I have been using are enabling sky light effect and PPLL rendering.
I was playing with an amount of samples in both, so it looks good enough and doesn't take ages to render (that hair blueprint will have a serious viewport performance impact ), and Sky Lighting Distance Threshold, that's telling how much the sky light penetrates the hairs.
You can find full explanations in their YouTube video, where they explain all the settings in depth.
Summary
I find Houdini to be an insanely powerful tool for grooming. I am not going back to XGen. There is so much more control with it, but the real skill and struggle is to actually be able to make Houdini do exactly what you want, so you can replicate references as closely as possible. That's the next goal: to make a hairstyle after an existing one much closer than roughly matching it.