Skip to content

How It Works

tcwilsonart edited this page Jun 13, 2026 · 2 revisions

How It Works

You don't need this page to use TensionFX — but it helps when you want to customize, debug a seam, or understand why the MetaHuman setup has extra steps.


The core idea: measure real edge-length change

A normal material/vertex shader sees each vertex in isolation — it can't compare a vertex to its neighbors, so it can't know if the surface is stretching. The only artist-facing place in Unreal that can read neighboring vertices on the GPU is the Deformer Graph. That's why TensionFX is a Deformer Graph.

Each frame, for every vertex, the graph:

  1. Looks at the vertex's connected neighbors.
  2. Compares each edge's current (deformed) length to its rest length.
  3. Averages those per-edge ratios into one signed number:
    • > 0 → stretch (edges longer than rest)
    • < 0 → squash (edges shorter than rest)
  4. Writes it to a vertex-color channel for the material to read.

Because it reads the final deformed positions, it automatically includes skinning, morph targets / ARKit blendshapes, and ML Deformer output — it doesn't care how the vertex moved, only that it did (relative to the mesh's rest pose). The measure is a mean of per-edge length ratios, which makes it scale-independent (works the same on a tiny mesh or a huge one).


The standard graph — DG_TensionFX_LBSkinning

The simple case. Conceptually:

Read Skinned Mesh ─┐ (rest positions)
LinearBlendSkin ───┤ (deformed positions)   ──►  Tension Kernel  ──►  Write Skinned Mesh
Connectivity ──────┘ (who neighbors whom)         (per-edge ratio)     (Color: R=stretch, G=squash;
                                                                        Position/Tangents passed through)
  • Read Skinned Mesh gives the rest/bind positions.
  • LinearBlendSkin gives the deformed positions (this is the step that applies the animation — rest comes from Read, deformed from here).
  • Connectivity provides each vertex's neighbor list.
  • The kernel does the ratio math and writes R = stretch, G = squash.
  • Position and tangents pass straight through so the mesh stays deformed and lit normally.

DG_TensionFX_Morph_and_MLDeformers is the same idea, set up to read deformation coming from morph targets / an ML Deformer.


The MetaHuman graphs — why they're bigger

DG_TensionFX_MetaHuman_Body and DG_TensionFX_MetaHuman_Head do the same tension measurement, plus two extra jobs that MetaHuman skin requires.

1. They move squash to Alpha

MetaHumans already use the green vertex-color channel as a recompute mask (see below), and blue is in use too. Stomping green would break their shading, so TensionFX keeps stretch in Red but writes squash to Alpha instead of Green.

2. They re-do the tangent/normal recompute (the important part)

Here's the catch that makes MetaHuman support non-trivial:

A Deformer Graph replaces the GPU Skin Cache. Unreal's masked "Recompute Tangents" pass lives inside that skin cache. So the moment any mesh deformer is active, the engine's built-in tangent recompute stops running — and stretched/morphed skin keeps stale normals, which looks wrong.

So the MetaHuman graphs recompute tangents themselves, the same way MetaHuman skin has always worked — gated by a per-vertex mask:

  • An open-edge (boundary) detection pass finds the mesh's open edges and marks everything else green. Green means "recompute allowed here."
  • Around seams/boundaries (green = 0), the original imported tangents are protected so seams don't pop.
  • A lerp blends recomputed normals (on the green vertices) against the protected imported normals (everywhere else) — exactly mirroring Epic's own recompute-tangents logic.

That's why you'll see those stages inside the MetaHuman graphs — open edges → blend (lerp) the mask → recompute/apply the tangent change — on top of the tension kernel.

The seam-under-heavy-stretch caveat is a separate, geometric issue, not a recompute one: head and body are different meshes, so the stretch measurement can't span the gap between them. See Troubleshooting.


Why you set "Default Mesh Deformer"

Assigning the graph as the skeletal mesh's Default Mesh Deformer is what switches the mesh's GPU path over to the graph (and, on MetaHumans, hands the tangent-recompute responsibility to it).


Back to: Use it on your own mesh · MetaHumans · Troubleshooting