Control SkinnedMesh Vertices with Bones in Three.js
In Three.js, skeletal animation is the standard method for animating
organic models like characters or flexible objects. This article
provides a direct, step-by-step guide on how to utilize
Bone objects to control the vertices of a
SkinnedMesh. You will learn how to configure vertex
skinning attributes, construct a skeletal hierarchy, and bind the
skeleton to a mesh so that moving the bones deforms the geometry in real
time.
1. Prepare the Geometry with Skin Indices and Weights
For a mesh to react to bones, its geometry must contain two specific
vertex attributes: skinIndex and
skinWeight.
- Skin Index (
skinIndex): Defines which bones (by their index in the skeleton array) influence a specific vertex. - Skin Weight (
skinWeight): Defines the percentage of influence (from 0 to 1) each of those bones has on the vertex.
Up to four bones can influence a single vertex. In code, you define
these attributes on a BufferGeometry object:
import * as THREE from 'three';
const geometry = new THREE.BufferGeometry();
// Define vertex positions
const positions = [
// ... vertex coordinates ...
];
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
// Define which bones affect each vertex (up to 4 bones per vertex)
const skinIndices = [
0, 0, 0, 0, // Vertex 0 influenced only by Bone 0
0, 1, 0, 0, // Vertex 1 influenced by Bone 0 and Bone 1
// ...
];
geometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices, 4));
// Define the weight of influence for each bone
const skinWeights = [
1, 0, 0, 0, // Vertex 0: 100% Bone 0
0.5, 0.5, 0, 0, // Vertex 1: 50% Bone 0, 50% Bone 1
// ...
];
geometry.setAttribute('skinWeight', new THREE.Float32BufferAttribute(skinWeights, 4));2. Create the Bone Hierarchy
Bones are specialized Object3D nodes. You construct a
skeleton by parenting bones to one another to form a hierarchical
tree.
const rootBone = new THREE.Bone();
const childBone = new THREE.Bone();
// Position the child bone relative to the root bone
childBone.position.y = 5;
// Build the hierarchy
rootBone.add(childBone);
// Store the bones in a flat array
const bones = [rootBone, childBone];3. Create and Bind the SkinnedMesh
Once you have your geometry, material, and bone array, you can
instantiate a SkinnedMesh and a Skeleton. You
must add the root bone to the mesh (or the scene) so its transform
matrix updates correctly.
// Create a material that supports skinning
const material = new THREE.MeshStandardMaterial({ skinning: true });
// Instantiate the SkinnedMesh
const skinnedMesh = new THREE.SkinnedMesh(geometry, material);
// Create the Skeleton from the bone array
const skeleton = new THREE.Skeleton(bones);
// Bind the skeleton to the mesh
skinnedMesh.add(rootBone); // Add the root bone to the mesh
skinnedMesh.bind(skeleton); // Bind the skeleton4. Manipulate the Bones to Deform the Vertices
Once bound, any translation, rotation, or scaling applied to the
Bone objects will automatically calculate the new positions
of the vertices in the GPU shaders.
To animate or control the mesh deformation, simply update the properties of your bones in your render loop:
function animate() {
requestAnimationFrame(animate);
// Rotate the child bone over time
const time = Date.now() * 0.001;
childBone.rotation.z = Math.sin(time) * 0.5;
renderer.render(scene, camera);
}
animate();