October 7, 2020

Groom POM - Soft, Beautiful Fur with little effort

Closeup of Groom POM


Various Foxes Rendered in UE4

I think it's no surprise why so few games take a legitimate stab at trying to replicate real fur on characters. Rendering millions of individual fur strands are impractical, and keeping things basic tends to yield the most appealing results. The Billie Bust Up example has the most appeal, but makes no attempt to capture texture. Similar for the Poly Art example. Epic's own Fennix from Fortnite balances appeal and resolve, but mostly uses medium-sized polygons to capture tufts of fur rather than worrying about texture. FostAPK tried a more realistic texture on a flat model, but it becomes quite obvious the fur is fake and the appeal goes down. The most realistic approaches use shells to make an attempt at generating the appearance of strands of fur leaving the model, but the results are often poor quality and unappealing.

Murnatan Aliens - AAAGames
Designed by Tomáš Drahoňovský
Using Neoglyphic's Neofur plugin
Developed by Carlos Montero et.al.

Of course, it's no secret the Neofur plugin by Neoglyphic Entertainment had the most fully resolved fur implementation in UE4. The fur was fully interactable on mobile, supporting plenty of grooming features and color options (including base-to-tip coloring on the strands), and it appears to support actual shells so the fur can have appropriate silhouettes. The character design is in the eye of the beholder, but the fur here is some of the most technically stunning ever accomplished, within the engine or otherwise.

Unfortunately, the group disbanded, the plugin stopped being supported, and it was pulled from the Marketplace years ago.

I set out to make my own fur solution. Strands are still too expensive and experimental, and shells require tampering with custom shader scripts and might be beyond my comprehension... so it had to be based on POM.

Groom POM
171 total pixel shader instructions
2-16 steps DTAA, 2.5-20 step average

Not bad?

This result is quite simple, it's just a combination of a few very good decisions. The heightfield texture is just a bunch of spots in random locations with a bit of distance between them.

Pros
  • Beautiful furry and fuzzy look with clear layering
  • Easy drag-and-drop results
    • Note - Direction relies on texture coordinate and UVs, which require careful consideration, but it can be dropped on any mesh for instant results without plugins.
  • Direction and length can be easily controlled by the artist
    • Can also be easily expanded with constants and custom flowmap/groom textures, even panning textures to simulate wind
  • Optimization is superb
    • Steps are reduced evenly across the surface with minimal distortion
    • DTAA gives impressive results with blurring/fuzziness as an appealing artifact for lower samples
    • Very low min steps (2-3) looks very good
    • 16 max steps looks satisfying. Diminishing returns with higher samples.
    • Not much more expensive than other, less interesting surfaces
  • Flow direction can be used for calculating tangents for anisotropy in shader (will experiment with this later)
Limitations
  • No exposed fur on silhouettes
    • Experiments show silhouettes are possible, but tend to leave gaping holes in the mesh when using Fresnel. It's possible to use 2 copies of the mesh, one solid with inward vertex displacement, another with masked translucency to provide silhouettes without disappearing completely. This would be more complicated to rig and animate properly. It would be easier to analytically calculate a proper solid zone in shader. Maybe with sobel edge detection? Custom depth? This needs more experimentation.
  • Distortion around the surface
    • Persistent issue with POM
  • Sharp hairline details do not look as good as softer fur - I.E. shiny wet black dog
    • Such examples should be textured flat on the model with anisotropy anyways.

Flow Map is actually a 3-Vector input. XY/RG controls the direction (in texture space), while Z/B controls the length.

Simply adding a 2-vector flow map or constant after the View Trace portion inside POM will allow you to change the direction of the fur. But because of the direction, Fresnel no longer works properly for sample optimization. I got rid of the Fresnel optimizer and substituted my own.


Visualization of Steps Optimizer in Groom POM
White = max steps, Black = min
Note more samples are used bottom left of mesh where strands appear longer. This method respects flow direction and strand length.


Steps Optimizer - 9 extra instructions vs. unoptimized

This optimizer converts the flow map into normals and compares to the camera angle. Short fibers, and fibers facing toward the camera receive less samples than long fibers facing perpendicular. The lack of a clamp after the dot term makes it possible for the samples to be increased beyond max if the direction is elongated (XY flow map determines direction across the surface, Z determines length). The added benefit of using the optimizer is a more consistent look across the surface - samples don't just build up in one part of the mesh more than the other.

Final look - Shadow, AO, and Fresnel in Cloth Shader

This is what completes the final look. The heightmap is used to generate a "shadow" from the base of the hairs pointing outward, making individual strands stand out. AO emphasizes this shading in the shadows. Higher roughness and the Fresnel term gives the fur a brighter sheen at grazing angles according to the light source, where light would be picked up by the fur.

It is possible to enhance this method further with a more robust setup capable of handling silhouettes. The flow direction should also be the basis for calculating tangents for anisotropy. But as it stands, this fur looks soft, luscious, and beautiful, it's extremely well optimized, and it's extremely easy for artists to work with and make changes.

Happy Fox by Ma-zach
This is not UE4. But it could be UE5...

Please credit me, Michael Fahel, if you intend to use these in a commercial project. And let me know! I'd love to see this technique in the wild!