如何用Three.js实现两个JSON格式3D模型的动画变形效果?
实现Three.js中两个3D模型的变形(Morph)动画
嘿,我来帮你搞定这个3D模型变形的问题!要实现两个模型之间的流畅Morph动画,核心前提得先明确——两个模型的顶点数量和顶点顺序必须完全一致!要是顶点数不一样或者建模时顶点被重排了,变形效果会直接崩掉,先确认你的两个JSON模型满足这个条件哈~
下面是具体的实现步骤:
1. 正确加载两个模型(注意加载顺序)
不要直接把两个模型都加到场景里,第二个模型是用来提取顶点数据当变形目标的,我们只需要把第一个模型留在场景中展示。可以用嵌套加载的方式确保顺序:
let model1, model2; const objectLoader = new THREE.ObjectLoader(); // 先加载第一个模型(显示在场景中的基础模型) objectLoader.load("assets/3dobjects/object1.json", function(obj) { model1 = obj; scene.add(model1); // 第一个模型加载完成后,再加载第二个模型 loadSecondModel(); }); // 加载第二个模型(仅用于提取顶点数据) function loadSecondModel() { objectLoader.load("assets/3dobjects/object2.json", function(obj) { model2 = obj; // 加载完成后设置变形目标和动画 setupMorphAnimation(); }); }
2. 给基础模型添加变形目标
接下来要把第二个模型的顶点数据作为变形目标,添加到第一个模型的几何体中,同时开启材质的变形支持:
function setupMorphAnimation() { // 假设你的模型是Mesh类型,取第一个子网格(如果模型本身就是Mesh,直接用model1.geometry) const baseMesh = model1.children[0]; const targetMesh = model2.children[0]; // 提取第二个模型的顶点数据 const targetVertices = targetMesh.geometry.attributes.position.array; // 创建变形目标对象,添加到基础模型的几何体中 baseMesh.geometry.morphTargets.push({ name: "targetObject2", vertices: new Float32Array(targetVertices) }); // 标记几何体需要更新,让Three.js识别新的变形目标 baseMesh.geometry.morphTargetsNeedUpdate = true; // 开启材质的变形目标支持,这一步很关键! baseMesh.material.morphTargets = true; }
3. 控制变形动画进度
现在可以通过修改morphTargetInfluences数组的值来控制变形程度了——数组的每个元素对应一个变形目标的权重,0是原始形态,1是完全变成目标模型。
原生Three.js动画循环实现
用requestAnimationFrame做基础的循环变形:
let morphProgress = 0; const morphSpeed = 0.005; // 调整这个值控制变形快慢 function animate() { requestAnimationFrame(animate); // 让进度在0-1之间循环,实现来回变形 morphProgress = (morphProgress + morphSpeed) % 1; // 给第一个变形目标设置权重(我们只加了一个,所以索引是0) model1.children[0].morphTargetInfluences[0] = morphProgress; renderer.render(scene, camera); } // 启动动画循环 animate();
用缓动库实现更流畅的动画
如果想要更丝滑的缓动效果(比如慢进慢出),可以用Tween.js或者GSAP这类库,举个Tween.js的例子:
// 先引入Tween.js库 new TWEEN.Tween({ progress: 0 }) .to({ progress: 1 }, 2000) // 2秒从原始形态变到目标形态 .easing(TWEEN.Easing.Quadratic.InOut) // 缓动函数 .onUpdate(function() { model1.children[0].morphTargetInfluences[0] = this.progress; }) .repeat(Infinity) // 无限循环 .yoyo(true) // 来回变形(从原始到目标再回到原始) .start(); // 记得在动画循环里更新Tween function animate() { requestAnimationFrame(animate); TWEEN.update(); renderer.render(scene, camera); } animate();
关键注意事项
- 顶点一致性:敲黑板!这是变形成功的核心,两个模型必须从同一个基础模型修改而来,导出时要关闭建模软件的顶点合并、优化等功能,确保顶点数和顺序完全匹配。
- 多子网格处理:如果你的模型包含多个子网格,要逐个给每个子网格添加对应的变形目标,不能只处理一个。
- 性能优化:变形动画会增加GPU负载,如果模型顶点数很多,可以考虑简化模型或者使用
BufferGeometry(现在Three.js默认就是这个,一般没问题)。
内容的提问来源于stack exchange,提问作者cs.matyi




