You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用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

火山引擎 最新活动