Course outline
Morph Targets and Skinning
In one line
Morph targets blend a mesh between stored shapes. Skinning bends a mesh with a skeleton of bones. You animate both, and see what goes wrong when a skinned mesh is cloned the wrong way.
Think of it like this
A clay ball can be pressed into a cube or stretched into an egg. A morph target is a photo of the clay in each shape, and you slide between photos. A skinned mesh is a glove puppet: bend the finger inside and the cloth follows.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- morph target
- A second (or third...) set of vertex positions for the same mesh, blended in with an influence from 0 to 1.
- influence
- How much of a morph target is applied. 0 none, 1 all.
- bone
- An Object3D in a tree used to bend a skinned mesh. Moving a bone moves the vertices that are weighted to it.
- skeleton
- The list of bones of one SkinnedMesh, plus their rest pose.
- skinning weights
- For each vertex, how strongly each of (up to four) bones pulls it.
Step by step
01Make two targets
The first example builds a sphere, then two extra position sets: the same points pushed out to a cube, and the points stretched tall. ball.updateMorphTargets() creates the influences, [0, 0].
02Animate the influences
A clip with two tracks raises cube to 1 and tall to 0.5 at 1 s and back to 0 at 2 s. At 1 s the log reads 1.00, 0.50.
03The late-target trap
A second mesh gets its target after it was made: its morphTargetInfluences is undefined, until updateMorphTargets() makes [0].
04Three foxes
The second example loads the Fox, then clones it two ways. It animates only the SkeletonUtils clone and checks both the bones and the picture.
Try it yourself
- 1
Overshoot
In the morph example change the
cubetrack values[0, 1, 0]to[0, 2, 0]. At 1 s the log prints2.00, 0.50: influences are not clamped, so the shape goes past the cube. - 2
Hide the original
In the fox example add
original.visible = false;after the loop that adds the foxes. A fox is still drawn on the left. It is the middle one, drawn at the original's place: its mesh uses the original's bones, and hiding the original does not stop those bones updating.
Code & diagrams
Watch the sphere flatten into a cube and stretch. Each target is a full copy of the 561 vertex positions.
Console
Run the scene to see its console.Expected console output
morph targets: 2 | influences: [ 0, 0 ]
at 0 s: influences 0.00, 0.00
at 1 s: influences 1.00, 0.50
at 2 s: influences 0.00, 0.00
targets added too late: influences = undefined
after updateMorphTargets(): [ 0 ]Left: the original. Middle (x = 0): `clone()`. Right: `SkeletonUtils.clone()`, the only one that runs. The middle fox is not drawn where it was placed. Fox credits: Topic 9.3.
Console
Run the scene to see its console.Expected console output
clone() shares the original's bones: true
SkeletonUtils.clone has bones of its own: true
tail bone z, original / clone() / SkeletonUtils: -0.054 -0.054 0.258
is a fox drawn at...
x = -1.2 original yes
x = 0 clone() no (background)
x = 1.2 SkeletonUtils.clone yesBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Clone it the usual way
In the fox example change SkeletonUtils.clone(original) to original.clone().
Myth vs fact
Myth
Skinning moves the mesh's vertices on the CPU.
Fact
The CPU moves the bones. The GPU moves each vertex using its bone indexes and weights, in the vertex shader.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Bones are ordinary
Object3Dnodes, so you can attach things to them:bone.add(hat)makes the hat follow the fox's head. - ▸
glTF allows 4 bone influences per vertex in
JOINTS_0andWEIGHTS_0. More than that needs extra sets, which three.js does not read. - ▸
A hand-built target, as in the example, holds absolute positions. Targets that come from a glTF file hold offsets from the base shape, and the loader sets
geometry.morphTargetsRelativefor you.
Remember this
- 1
The problem this solves: the tools so far move a whole object (Topic 9.1) or turn parts that are separate meshes (the wheels). A face smiling or a fox running changes the shape of one mesh. Moving every vertex by hand would be slow and hard to author.
- 2
Morph targets store extra sets of vertex positions on the geometry (
geometry.morphAttributes.position). The mesh holds one number per target,mesh.morphTargetInfluences. The drawn vertex is the base plus each influence times its offset: 0 is the base shape, 1 is the full target, and 0.5 is halfway. Values above 1 push past the target, as the example's tryIt shows. three.js stores them in a texture, so a mesh can have many. - 3
Naming.
mesh.morphTargetDictionarymaps names to indexes. glTF files carry them (a face hassmile,blink). With names a track readsball.morphTargetInfluences[cube]. Without the dictionary, use the index. - 4
The trap:
morphTargetInfluencesis created by theMeshconstructor only if the geometry already has morph attributes. Add them afterwards and the array isundefined: callmesh.updateMorphTargets(). - 5
Skinning. A
SkinnedMeshhas aSkeleton: a list ofBoneobjects, which are ordinaryObject3Dnodes arranged in a tree (hip, spine, neck, tail). Each vertex stores up to four bone indexes and four weights (skinIndex,skinWeight), and is drawn at the weighted mix of those bones' movements. Turn a bone and the skin near it follows. The animation clips from Topic 9.3 move the bones, not the vertices. - 6
Cloning.
object.clone()copies the object tree, but a clonedSkinnedMeshstill points at the *original's* skeleton. The clone is drawn wherever the original's bones are, ignoring its own position, and animating the clone's copied bones changes nothing you can see. UseSkeletonUtils.clone(object)fromthree/addons/utils/SkeletonUtils.js: it copies the bones and binds the new mesh to them. - 7
Cost. Both techniques run on the GPU, but morph targets cost memory for each target and skinning costs a matrix per bone per mesh. The animation itself runs on the CPU in the mixer.
Explain it without notes
When would you use a morph target and when a skeleton?
Why does a skinned mesh made with object.clone() appear in the wrong place?
Practice
Make three independent foxes that play different clips.
A mesh has morph targets added after it was created. Make morphTargetInfluences available.
Done when you can
Say what a morph target and a bone each change
Animate morph target influences with a clip
Clone a skinned model with
SkeletonUtils.cloneRecognise a mesh that uses its original's skeleton