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.

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 skeleton

4. 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();