Three.js 快速入门教程【十三】外部模型加载后常见的处理操作

请添加图片描述

系列文章目录

Three.js 快速入门教程【一】开启你的 3D Web 开发之旅
Three.js 快速入门教程【二】透视投影相机
Three.js 快速入门教程【三】渲染器
Three.js 快速入门教程【四】三维坐标系
Three.js 快速入门教程【五】动画渲染循环
Three.js 快速入门教程【六】相机控件 OrbitControls
Three.js 快速入门教程【七】常见几何体类型
Three.js 快速入门教程【八】常见材质类型
Three.js 快速入门教程【九】光源类型
Three.js 快速入门教程【十】常见的纹理类型
Three.js 快速入门教程【十一】天空盒的多种实现方式
Three.js 快速入门教程【十二】外部模型加载
Three.js 快速入门教程【十三】外部模型加载后常见的处理操作
Three.js 快速入门教程【十四】使用Stats.js监控渲染帧率和性能优化
Three.js 快速入门教程【十五】交互神器DragControls使用详解,实现对物体或模型拖拽
Three.js 快速入门教程【十六】调试神器 gui.js使用详解,可视化面板控制场景参数提高开发效率
Three.js 快速入门教程【十七】射线拾取模型——射线与射线投射器Raycaster介绍
Three.js 快速入门教程【十八】射线拾取模型——鼠标点击屏幕选中模型或物体
Three.js 快速入门教程【十九】CSS2D渲染器介绍,实现场景中物体或设备标注标签信息
Three.js 快速入门教程【二十】三维模型优化实战:使用gltf-pipeline与Draco对模型进行压缩,提高加载速度和流畅性
Three.js 快速入门教程【二十一】CSS3D渲染器(CSS3DRenderer、CSS3DObject 、CSS3DSprite)介绍,实现场景中物体标注标签信息
Three.js 快速入门教程【二十二】动画神器Tween.js使用指南



一、前言

      在使用 Three.js 进行前端 3D 开发时,加载外部模型是一项非常重要的功能。当我们成功加载外部模型后,还需要对其进行一系列的处理,以满足我们的项目需求。本文将详细介绍在 Three.js 中加载外部模型后常见的处理操作


二、traverse 方法

traverse 方法是Object3D(模型对象父类是Object3D)上的方法,它允许你对场景中的每个对象执行特定的操作,无论该对象嵌套得有多深。它会递归地访问对象及其所有子对象,从而确保你可以对场景中的每个对象进行处理。这在需要对场景中的所有对象应用相同的操作(如更改材质、设置可见性等)时非常有用。

方法定义

Object3D.traverse(callback: Function): void
  • 递归遍历:自动遍历对象及其所有子对象

  • 执行回调:对每个节点执行自定义处理

  • 继承特性:所有继承自Object3D的对象都具备该方法

以加载gltf格式模型为示例:

// 递归遍历所有模型节点
gltf.scene.traverse(function(obj) {
//是否是网格模型
    if (obj.isMesh) {
        //对网格模型进行修改操作
        ....
        ...
    }
});

ps:外部模型可能是一个大模型由很多个子网格模型(Mesh)组合而成,可以通过traverse 方法遍历子网格模型(Mesh)进行相应的修改操作


三、加载外部模型后常见的处理操作

3.1 模型缩放并居中使得完整显示

建模时的如果坐标原点不是模型几何中心加载完后模型位置会偏移原点,模型尺寸过大或过小首次加载完不能完整展示影响视觉效果,可以按照以下步骤实现居中和完整显示:

实现思路:

  • 计算模型边界框:通过 THREE.Box3 计算模型的边界框,进而得到模型的大小和中心位置。
  • 缩放模型:根据场景的尺寸和模型的大小,计算合适的缩放比例,将模型缩放到合适的大小以完整显示。
  • 移动模型到原点中心:将模型的中心位置移动到原点。
       import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
       
  // 创建GLTF加载器
        const loader = new GLTFLoader();
        // 加载GLTF模型
        loader.load(
            'test_model.gltf', // 替换为你的模型文件路径
            (gltf) => {
            let model = gltf.scene;
              /**
             * 自动调整模型大小和位置
             * */ 
            // 计算模型的边界框
             const box = new THREE.Box3().setFromObject(model);
            //获取模型中心点
             const center = box.getCenter(new THREE.Vector3());
            // 计算模型的尺寸
             const size = box.getSize(new THREE.Vector3());
            // 计算缩放比例,使模型完整显示
             const maxDim = Math.max(size.x, size.y, size.z);
             const scale = 20 / maxDim;//20可以根据实际情况调整数值,控制模型显示的大小
              // 缩放模型
              model.scale.setScalar(scale);
              let newVector3 = center.multiplyScalar(scale);
              // 将模型移动到原点中心
              model.position.sub(newVector3);
              scene.add(model);
            },
            undefined,
            (error) => {
                console.error('Error loading model:', error);
            }
        );

运行效果:

调整前:
在这里插入图片描述

调整后:

在这里插入图片描述


3.2 材质的调整

外部模型自带的材质可能不符合我们的设计风格,或者我们需要在特定条件下改变材质。

以将模型的材质更换为MeshBasicMaterial为例:

const newMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 创建新材质,颜色为绿色
model.traverse((child) => {
    if (child.isMesh) {
        child.material = newMaterial;
    }
});

3.2.2 修改纹理

按需修改模型纹理

loader.load(
  'test_model.gltf', // 替换为你的模型文件路径
  (gltf) => {
    let model = gltf.scene;
    //遍历子模型
    model.traverse((child) => {
      //网格模型
      if (child.isMesh) {
      //创建纹理
        let texture = new THREE.TextureLoader().load("/texture.jpg");
        texture.encoding = THREE.sRGBEncoding;
        //设置纹理
        child.material.map= texture
      }
    });
    scene.add(model);
  },
  undefined,
  (error) => {
    console.error(error);
  }
);

运行效果:

在这里插入图片描述

3.2.2 材质属性校准

模型在导出时可能会丢失部分材质属性,需要重新配置基础材质参数:

model.traverse((child) => {
  if (child.isMesh) {
    child.material.metalness = 0.5; // 统一金属度
    child.material.roughness = 0.3; // 统一粗糙度
    child.material.needsUpdate = true; // 强制更新材质
  }
});

3.2.3 透明材质处理

对于含alpha通道的材质需特别处理:

model.traverse((child) => {
  if (child.isMesh) {
    child.material.transparent = true;
    child.material.alphaTest = 0.1; // 防止边缘闪烁
    child.material.depthWrite = false; // 优化渲染顺序
  }
});


3.3 动画的添加

如果模型本身带有动画数据(如通过 FBX 等格式导入的带有骨骼动画的模型),我们可以使用AnimationMixer来控制和播放动画。

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";

// 创建动画混合器
let mixer = null;
// 加载GLTF模型
let loader = new GLTFLoader();
loader.load(
  'test_model.gltf', // 替换为你的模型文件路径
  (gltf) => {
    let model = gltf.scene;
    // 获取动画混合器
    mixer = new THREE.AnimationMixer(model);
    //模型添加到场景
    scene.add(model);
    // 获取动画
    const clips = gltf.animations;
    if (clips) {
      clips.forEach((clip) => {
        // 根据需要播放特定的动画片段
        const action = mixer.clipAction(clip);
        action.play(); // 播放动画
      });
    }
  },
  undefined,
  (error) => {
    console.error(error);
  }
);
//创建时钟
const clock = new THREE.Clock();

// 渲染循环动画
function animate() {
  requestAnimationFrame(animate);
  // 如果有动画混合器,更新动画
  if (mixer) {
    // 获取自上一帧以来的时间差
    const delta = clock.getDelta();
    mixer.update(delta);
  }

  renderer.render(scene, camera);
}

// // 执行动画
animate();

运行效果:

请添加图片描述


3.4 阴影的添加

使得模型产生阴影效果,可按如下方法批量给子模型添加:

// 加载GLTF模型
let loader = new GLTFLoader();
loader.load(
   'test_model.gltf', // 替换为你的模型文件路径
  (gltf) => {
    let model = gltf.scene;
    //遍历子模型
    model.traverse((child) => {
      //网格模型
      if (child.isMesh) {
        child.castShadow = true; // 让模型产生阴影
        child.receiveShadow = true; // 让模型接受阴影
      }
    });
    scene.add(model);
  },
  undefined,
  (error) => {
    console.error(error);
  }
);

除了给子模型添加产生和接受阴影,想要实现阴影效果还不要忘记配置渲染器、光源和地面对阴影的支持

(1)启用渲染器阴影支持

const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true; //  必须开启
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔化阴影边缘

(2) 配置光源投射阴影

// 以点光源为例
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.castShadow = true; //  光源投射阴影
pointLight.position.set(5, 8, 3);

(3) 地面接收阴影

const floor = new THREE.Mesh(
  new THREE.PlaneGeometry(20, 20),
  new THREE.MeshPhongMaterial({ color: 0x808080 })
);
floor.receiveShadow = true; // 地面接收阴影
floor.rotation.x = -Math.PI / 2;

运行效果:
在这里插入图片描述


3.5 模型的分组与管理

当场景中有多个模型时,为了便于管理和操作,我们可以将相关的模型进行分组。

const group = new THREE.Group();
group.add(model1);
group.add(model2);
scene.add(group);

这样,对group对象进行操作(如移动、旋转、缩放等)时,组内的所有模型都会相应地进行变化。


五、总结

       通过以上对加载外部模型后的常见处理操作,我们可以更好地在 Three.js 项目中使用外部模型,实现更加丰富和交互性强的 3D 应用。在实际开发中,还需要根据具体的项目需求进行更多的优化和调整。

更多three.js入门知识点请关注该系列教程后续的更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

pixle0

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值