[UE Deformer Graph] partial application of the deformer graph
Documentation of Unreal nodes Skinned Mesh Vertex Attributes
and Skin Weights as Vertex Mask


Skinned Mesh Vertex Attributes
Let's use the Skinned Mesh Vertex Attributes
node of the deformer graph, this node allows you to read the value of a weight map painted over the surface of your model:

(In red each vertex is assigned 1.0f
and the rest 0.0f
)
Let's open up a skeletal mesh with the skeletal mesh editor:

First we need to create a per vertex attribute buffer, in New Attribute
section type in your attribute name and hit Add Weight Map Layer

It should show up now in the Vertex Attributes Inspector
:

We can move on to the Paint Maps
tab and start painting the weight map:

Now go ahead and paint the area you'd like to deform:

Don't forget to push Apply
otherwise changes will be discarded when leaving the menu.

For the deformer graph asset to access this new buffer it seems necessary to check import vertex attributes
:

Go to a deformer asset, for instance /All/EngineData/Plugins/DeformerGraph/Deformers/DG_LinearBlendSkin

Let's modify to try out our new weight map, first add Skinned Mesh Vertex Attributes
and connect it to your kernel:

Click on the node Skinned Mesh Vertex Attributes
and set the name of the attribute in the detailed pannel


Now in your compute shader kernel code, for instance LBS, you can customize it to only deform when the weight map equals 1.0:
if (Index >= ReadNumThreads().x) return; float3 Position = ReadPosition(Index); float4 LocalTangentX = ReadTangentX(Index); float4 LocalTangentZ = ReadTangentZ(Index); float3x4 WeightedBoneMatrix = ReadWeightedBoneMatrix(Index); // Compute Skin float3 SkinnedPosition = mul(WeightedBoneMatrix, float4(Position, 1)); float4 SkinnedTangentX = float4(normalize(mul((float3x3)WeightedBoneMatrix, LocalTangentX.xyz)), LocalTangentX.w); float4 SkinnedTangentZ = float4(normalize(mul((float3x3)WeightedBoneMatrix, LocalTangentZ.xyz)), LocalTangentZ.w); float3 Save = SkinnedPosition; float w = ReadValue(Index); // Weight Map // lerp between LBS position and input rest pose position: SkinnedPosition = (1.0 - w) * SkinnedPosition.xyz + w * Position.xyz; SkinnedTangentX = float4( (1.0 - w) * SkinnedTangentX.xyz + w * LocalTangentX.xyz, LocalTangentX.w); SkinnedTangentZ = float4( (1.0 - w) * SkinnedTangentZ.xyz + w * LocalTangentZ.xyz, LocalTangentZ.w); // Set Data WriteOutPosition(Index, SkinnedPosition); WriteOutTangentX(Index, SkinnedTangentX); WriteOutTangentZ(Index, SkinnedTangentZ);
And voila! Your model's vertices should only display animation for areas that were not painted

Here we did not took care of smoothing the transition between the head and the body, so the weight maps abruptly changes from 1 to 0.
LOD
I haven't looked much into LODs but you should check if Skinned Mesh Vertex Attributes
works properly for all LOD levels (it wasn't for me at the time of writing). Especially if the LOD level was not auto-generated by UE but manually imported.
Skin Weights as Vertex Mask
Instead of manually painting a weight map, you can generate one using the underlying skin weights of your model.
For instance, considering a subset of bones and their skin weights, and make the union of all these skin weight merged into a single weight map.
This what Skin Weight as Vertex Mask
allows to do

Here are the available settings:

Output: float Mask
a weight map (associates a float to each vertex).
Input:
- Skin Weight Profile
if empty use the default skin weights of the skeletal mesh model.
Otherwise use the extra skin weight profile defined in the skin profile
section of the skeletal mesh editor.
- Bone Names
list of bones to extract the weight map from.
- Expand Towards Leaf
as the name indicates from bones in Bone Names
we include leaf joints up to a point.
Below are examples with various values of Expand Towards Leaf
and Bone Names
set to the root joint of the model:

Expand Leaf: 0

Expand Leaf: 3

Expand Leaf: 10

Expand Leaf: 999
As you can see when Debug Draw Included Bones
is checked it displays the joints included in the generation of the weight map. Here the weight map disables skinning when set to 1
. Root joint has no influence over the mesh so the deformation stays untouched. Level 3 expands
the area to 1.0 for part of the hip and thighs since the root joint is connected to hip joint and thigh joints. Level 999 includes all joints the model is undeformed and displayed in rest pose.
- Expand Towards Root
is as expected the same but probagates the influence towards the root joint. Below it is set to 3
with Bone Names
set to "spine_05":

Mask computation
Below is the shader function that computes the mask. Skin weights are simply added together for any bone that is selected. In terms of cost the mask will generate a new "BoneIsSelected" buffer with the same structure and size of a skin weight buffer. An additional cost to loop trough and blend skin weights together is also incurred. So there might be some room to optimize this and have the vertex mask entirely computed on CPU, that would reduce memory and GPU computation cost.
float ReadMask_{DataInterfaceName}(uint VertexIndex) { #if !ENABLE_DEFORMER_BONES return 0; #else float MaskValue = 0; uint NumBones = ReadNumBones_{DataInterfaceName}(VertexIndex); uint BoneIndex = 0; for (BoneIndex = 0; BoneIndex < NumBones; BoneIndex++) { float BoneWeight = ReadBoneWeight_{DataInterfaceName}(VertexIndex, BoneIndex); uint BoneIsSelected = ReadBoneIsSelected_{DataInterfaceName}(VertexIndex, BoneIndex); MaskValue += BoneIsSelected ? BoneWeight : 0; } return MaskValue; #endif }
No comments