如何为GLTF/OBJ模型节点的材质透明度添加淡入淡出动画?
GLTF模型材质透明度动画实现方案
问题描述
我试过多种方法都没用,手里有个GLTF模型,已经拿到带网格的节点,节点也有材质,但不知道怎么给材质的透明度值加动画?
另外还有个相关问题:给场景里带材质的OBJ模型加动画没问题,但网格和材质一多,这种方法就乱得不行,有没有解决思路?
尝试过的方案
方案1
if (node.isMesh) { node.material.transparent = node.material.opacity < 1.0; node.material.needsUpdate = true; // 报错:node.setAttribute is not a function node.setAttribute('animation', { property: "material.opacity", from: 0, to: 1, dur: 2000, easing: "easeInQuad", loop: false, }); }
方案2
if (node.isMesh) { node.material.transparent = node.material.opacity < 1.0; node.material.needsUpdate = true; // 报错:node.appendChild is not a function const animation = document.createElement("a-animation"); animation.setAttribute("attribute", "material.opacity"); animation.setAttribute("from", "0"); animation.setAttribute("to", "1"); animation.setAttribute("dur", "2000"); animation.setAttribute("easing", "easeInQuad"); animation.setAttribute("loop", "false"); node.appendChild(animation); }
解决思路
核心问题说明
你之前的错误是把Three.js的Mesh对象当成了DOM元素来操作——setAttribute和appendChild是浏览器DOM节点的方法,Three.js的3D对象并没有这些API,得用Three.js自身的动画系统或者第三方补间库来实现。
方法一:使用Three.js原生Animation系统
利用Three.js的AnimationMixer、AnimationClip和KeyframeTrack来创建材质透明度动画,适合单网格或少量网格的场景:
// 假设node是Three.js的Mesh实例,已引入Three.js核心库 const mixer = new THREE.AnimationMixer(node); // 创建透明度关键帧轨道:时间点(秒)对应透明度值 const opacityTrack = new THREE.NumberKeyframeTrack( '.material.opacity', // 属性路径 [0, 2], // 时间轴:0秒到2秒 [0, 1] // 对应透明度:从0到1 ); // 创建动画片段 const clip = new THREE.AnimationClip('opacityAnim', -1, [opacityTrack]); // 创建动画动作并配置 const action = mixer.clipAction(clip); action.setEasing(THREE.EaseInQuad); // 设置缓动函数 action.play(); // 必须在渲染循环中更新动画混合器 const clock = new THREE.Clock(); function renderLoop() { requestAnimationFrame(renderLoop); const deltaTime = clock.getDelta(); mixer.update(deltaTime); renderer.render(scene, camera); } renderLoop();
方法二:用Tween.js批量处理多材质动画
如果场景中有大量网格和材质,推荐用Tween.js做批量动画管理,代码更简洁易维护:
// 先引入Tween.js库 import * as TWEEN from '@tweenjs/tween.js'; // 收集所有需要添加透明度动画的材质 const targetMaterials = []; scene.traverse((child) => { if (child.isMesh && child.material) { child.material.transparent = true; // 开启透明 child.material.needsUpdate = true; // 标记材质需要更新 targetMaterials.push(child.material); } }); // 创建批量补间动画 new TWEEN.Tween({ currentOpacity: 0 }) .to({ currentOpacity: 1 }, 2000) // 2秒内从0过渡到1 .easing(TWEEN.Easing.Quadratic.In) // 缓动函数 .onUpdate((obj) => { // 批量更新所有目标材质的透明度 targetMaterials.forEach(material => { material.opacity = obj.currentOpacity; }); }) .start(); // 在渲染循环中更新Tween function renderLoop() { requestAnimationFrame(renderLoop); TWEEN.update(); renderer.render(scene, camera); } renderLoop();
关键注意事项
- 必须将材质的
transparent属性设为true,否则opacity属性不会生效。 - 如果使用PBR材质(如
MeshStandardMaterial),建议根据需求调整depthWrite和depthTest属性,避免透明物体渲染顺序异常。 - 如果多个网格共享同一个材质,修改材质透明度会影响所有使用该材质的网格,若需要单独动画,需先克隆材质:
child.material = child.material.clone();
内容的提问来源于stack exchange,提问作者Millefolium Ink
相关产品推荐
相关产品推荐

