Loading...
Loading...
Debugging and fixing VRM SpringBone physics issues in three-vrm, including hair/clothing physics that flies upward, sticks out horizontally, or behaves unnaturally.
npx skill4agent add project-n-e-k-o/n.e.k.o vrm-springbone-physics@pixiv/three-vrmvrm.update(delta)// Add this to your animation loop
console.log('delta:', delta);
// Should be ~0.016 for 60fps, NOT 16 or larger!// Correct implementation using THREE.Clock
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
let delta = clock.getDelta();
// Clamp to prevent explosion on tab switch or lag
delta = Math.min(delta, 0.05); // Max 50ms
if (vrm) {
vrm.update(delta);
}
renderer.render(scene, camera);
}const distance = length - objectRadius - this.radius; // radius not scaled by world matrix"We do not recommend using SpringBone and scaling together"
[!NOTE] 50% reduction fixes ALL tested models. The exact mathematical reason is uncertain - the export scaling could vary by model/tool. However, this factor works universally in practice.
const colliders = Array.from(springBoneManager.colliders || []);
colliders.forEach(c => {
if (c.shape?.radius) c.shape.radius = 0;
});
// If hair now falls correctly, colliders were the issueconst REDUCTION_FACTOR = 0.5; // Compensates for UniVRM export scaling bug
const colliders = Array.from(springBoneManager.colliders || []);
colliders.forEach(collider => {
if (collider.shape?.radius > 0) {
// Save original for potential future adjustment
if (collider._originalRadius === undefined) {
collider._originalRadius = collider.shape.radius;
}
collider.shape.radius = collider._originalRadius * REDUCTION_FACTOR;
}
});const colliders = Array.from(springBoneManager.colliders || []);
colliders.forEach(collider => {
if (collider.shape?.radius !== undefined) {
collider.shape.radius = 0;
}
});colliders.forEach(collider => {
const boneName = collider.bone?.name?.toLowerCase() || '';
if (boneName.includes('head') || boneName.includes('face')) {
collider.shape.radius = 0;
}
});VRMSpringBoneColliderGroupvrm.scene.scalefunction scaleVRMScene(vrm, scaleFactor) {
// Scale the scene
vrm.scene.scale.setScalar(scaleFactor);
// Scale all collider radii to match
const springBoneManager = vrm.springBoneManager;
if (springBoneManager) {
const colliders = Array.from(springBoneManager.colliders || []);
colliders.forEach(collider => {
if (collider.shape?.radius !== undefined) {
// Store original radius if not already stored
if (collider._originalRadius === undefined) {
collider._originalRadius = collider.shape.radius;
}
// Scale radius with scene
collider.shape.radius = collider._originalRadius * scaleFactor;
}
});
}
}vrm.scene_worldSpaceBoneLength: 0[!CAUTION] Empirical Fix Notice: Thevalue is empirically determined from testing multiple VRM models. While the underlying UniVRM bug is documented, we cannot mathematically prove 50% is correct for all models. If you encounter hair physics issues, adjust this value first.COLLIDER_REDUCTION = 0.5
function initializeVRMPhysics(vrm) {
const springBoneManager = vrm.springBoneManager;
if (!springBoneManager) return;
// Reduce collider radii to compensate for UniVRM export bug (#673)
// This is an EMPIRICAL fix - adjust if needed
const COLLIDER_REDUCTION = 0.5;
const colliders = Array.from(springBoneManager.colliders || []);
colliders.forEach(collider => {
if (collider.shape?.radius > 0) {
collider._originalRadius = collider.shape.radius;
collider.shape.radius *= COLLIDER_REDUCTION;
}
});
console.log(`[VRM] Applied ${COLLIDER_REDUCTION * 100}% collider reduction to ${colliders.length} colliders`);
}
// Animation loop with delta clamping
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
let delta = clock.getDelta();
delta = Math.min(delta, 0.05); // Prevent physics explosion
if (vrm) {
vrm.update(delta);
}
renderer.render(scene, camera);
}| Method | Purpose |
|---|---|
| Clear physics state, return to initial positions |
| Capture current position as new "rest" state |
| Set of all SpringBone joints |
| Set of all colliders |
| Update all VRM systems including physics |
| Property | Description |
|---|---|
| Spring force (0 = no spring, 1 = stiff) |
| Gravity strength |
| Vector3 gravity direction (usually 0, -1, 0) |
| Damping (0 = no drag, 1 = full stop) |