Morph targets
Open the live demo · Read the source · View on GitHub
A morph target is a set of deltas: how far each vertex moves, added to its base position by some amount from nought to one. This page builds a flat sheet and one target that lifts its middle into a bump.
Step 1: Deltas for one target #
The target is the same shape as the mesh, but its numbers are offsets rather than positions. Here only Y moves, most in the middle and less toward the edges, which is what a gaussian falloff gives for free.
const int segments = 16;
final MeshData flat = const PlaneShape(
width: 2.0,
depth: 2.0,
widthSegments: segments,
depthSegments: segments,
).build();
final Float32List bump = Float32List(flat.vertexCount * 3);
for (var v = 0; v < flat.vertexCount; v++) {
final Vector3 p = flat.positionAt(v);
final double falloff = _gaussian(p.x, p.z);
bump[v * 3 + 1] = falloff * 0.9;
}
final MeshData withBump = flat.withMorphTargets(<MorphTarget>[
MorphTarget(vertexCount: flat.vertexCount, positions: bump, name: 'bump'),
]);
Step 2: Pack the deltas into a texture #
The deltas do not travel as a second set of vertex attributes: MorphTexture
packs them into a float texture instead, one column a vertex, so a lighting
model does not need a second vertex shader to read them.
final MorphTexture packed = MorphTexture.pack(withBump)!;
final TextureHandle? deltaTexture = context.device.createTextureFromPixels(
width: packed.width,
height: packed.height,
format: TextureFormat.r32g32b32a32Float,
pixels: packed.bytes,
);
Step 3: Weigh it #
MorphState holds how much of each target is showing, from nought to one,
and it lives on the node rather than on the mesh: two copies of one sheet can
wear different bumps from the same uploaded deltas.
sheet.morph = MorphState(
texture: deltaTexture,
targetCount: packed.targetCount,
reaches: packed.reaches,
)..setWeights(<double>[weight]);
Step 4: Change the weight while it runs #
_sheet?.morph?.setWeights(<double>[weight]);
Drag Weight from nought to one and watch the bump rise out of a flat sheet. At nought the sheet is exactly the base mesh; the deltas are being added, not substituted, so a weight of nought costs nothing and changes nothing.