threejs实现模型炸开全流程

模型工作

由于obj模型可能是一个整体,而不是一个个单独的部件,所以再炸开的时候,是无法进行部件分解的。因此,在搭建代码环境前,需要检查模型是否是可分解的模型。

在这里插入图片描述

上图为单一模型,没有分解部件,因此需要先在建模软件进行部件分解

主要流程为:

  • A键全选模型

  • tab键切换成编辑模式

  • 此时,鼠标选取一些部件的点,通过ctrl + l键进行部件关联
    在这里插入图片描述

  • 鼠标右键->分离->分离选中项,则键该部件进行分离

    在这里插入图片描述

  • 以此此操作,最终将整个模型部件进行分离,此时每个部件都有一个单独的名称

    代码环境搭建

    通过threejs搭建场景快捷便利,实现炸开效果,需要对单独的部件进行操作

    炸开效果

    主要流程:

    • 初始化三维场景,需要新建一个场景,设置背景光、基础灯光、轨道控制器等

    • 加载模型,调整模型姿态,相机姿态

    • 模型炸开:

      • 模型炸开需要考虑到一个炸开中心,以及各个部件的炸开方向

      • 一般选取模型中心点为炸开中心

      • 炸开方向可以通过每个部件的质心到炸开中心形成一个方向向量,以此向量形成炸开方向

      • 计算每个部件质心到炸开中心的距离,作为一个距离权重,以此保证在相似方向上的部件,离炸开中心越远,分离的距离也越远

          //收集所有部件信息
              model.traverse((child) => {
                //计算每个部件质心
                if (child.isMesh) {
                  //计算每个部件的质心
                  const geometry = child.geometry;
                  geometry.computeBoundingBox();
                  const partCenter = new THREE.Vector3();
                  geometry.boundingBox.getCenter(partCenter);
                  child.localToWorld(partCenter);
        
                  //计算基础方向向量
                  const direction = new THREE.Vector3()
                    .subVectors(partCenter, targetCenter)
                    .normalize();
                  // 添加随机偏移使爆炸效果更自然
                  const randomOffset = new THREE.Vector3(
                    (Math.random() - 0.5) * 0.3,
                    (Math.random() - 0.5) * 0.3,
                    (Math.random() - 0.5) * 0.3
                  );
        
                  // 计算距离权重(距离中心越远,移动距离越大)
                  const distanceWeight = partCenter.distanceTo(targetCenter) * 1;
                  parts.push({
                    mesh: child,
                    originalPosition: child.position.clone(),
                    originalRotation: child.rotation.clone(),
                    direction: direction.add(randomOffset).normalize(),
                    // direction: direction,
        
                    distanceWeight: distanceWeight,
                  });
                }
              });
        
      • 通过gsap动画,实现过程效果

        //炸开/复原切换函数
        const toggleExplode = () => {
          isExploded.value = !isExploded.value;
          parts.forEach((part) => {
            if (isExploded.value) {
              //炸开动画
              gsap.to(part.mesh.position, {
                x:
                  part.originalPosition.x + part.direction.x * part.distanceWeight * 15,
                y:
                  part.originalPosition.y + part.direction.y * part.distanceWeight * 15,
                z:
                  part.originalPosition.z + part.direction.z * part.distanceWeight * 15,
                duration: 1,
                ease: "power2.out",
              });
              //添加旋转动画
              gsap.to(part.mesh.rotation, {
                x: part.originalRotation.x + (Math.random() - 0.5) * Math.PI * 0.2,
                y: part.originalRotation.y + (Math.random() - 0.5) * Math.PI * 0.2,
                z: part.originalRotation.z + (Math.random() - 0.5) * Math.PI * 0.2,
                duration: 1,
                ease: "power2.out",
              });
            } else {
              //复原动画
              gsap.to(part.mesh.position, {
                x: part.originalPosition.x,
                y: part.originalPosition.y,
                z: part.originalPosition.z,
                duration: 1,
                ease: "power2.inout",
              });
        
              gsap.to(part.mesh.rotation, {
                x: part.originalRotation.x,
                y: part.originalRotation.y,
                z: part.originalRotation.z,
                duration: 1,
                ease: "power2.inOut",
              });
            }
          });
        };
        

        高亮效果

        涉及到相机的重定位和部件材质的替换

        主要流程:

        • 找到需要定位的材质

        • 存储其原有材质,重新定义新材质给它,以达到高亮效果

          //保存原始材质
            if (highlightedMesh.value) {
              //恢复之前高亮部件的材质
              highlightedMesh.value.material = originalMaterial.value;
            }
            //创建高亮材质
            originalMaterial.value = targetPart.material.clone();
            const highlightMaterial = targetPart.material.clone();
            
            // 调整为更柔和的蓝白色高亮,更适合金属质感
            highlightMaterial.emissive = new THREE.Color(0x3388ff); // 柔和的蓝色
            highlightMaterial.emissiveIntensity = 0.5; // 降低发光强度,使效果更自然
          
            // 如果需要更细腻的金属质感,可以同时调整这些参数
            highlightMaterial.metalness = 0.8; // 增加金属感
            highlightMaterial.roughness = 0.2; // 降低粗糙度,增加光泽感
            //应用高亮材质
            targetPart.material = highlightMaterial;
            highlightMaterial.value = targetPart;
          
        • 重新计算部件质心,相机指向新的位置并设置移动动画

          //计算部件的包围盒中心
            const boundingBox = new THREE.Box3().setFromObject(targetPart);
            const center = boundingBox.getCenter(new THREE.Vector3());
          
            //计算相机新位置
            const offset = new THREE.Vector3(2, 2, 2);
            const newCameraPosition = center.clone().add(offset);
            //使用GSAP制作相机移动动画
            gsap.to(camera.position, {
              x: newCameraPosition.x,
              y: newCameraPosition.y,
              z: newCameraPosition.z,
              duration: 2,
              ease: "power2.inOut",
              onUpdate: () => {
                camera.lookAt(center);
              },
            });
            //更新控制器目标点
            gsap.to(controls.target, {
              x: center.x,
              y: center.y,
              z: center.z,
              duration: 1,
              ease: "power2.inOut.inOut",
              onComplete: () => {
                controls.update();
              },
            });
          

    最终效果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水塔鸡丝

创作不易,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值