在Three.js中实现汽车的3D展示

汽车的3D展示是一个比较常见的应用,今天我就通过three.js来与大家分享如何实现一个非常简单的3D汽车展示场景。

前言

虽然例子很简单,但也需要您掌握基本的three.js知识点,例如场景、模型等基础。

感兴趣的可以先访问在线例子看看。

开始

废话不多说,现在就来开始进行代码的实践。

汽车创建

首先,我们需要使用GLTF/GLB模型加载器来将模型加载到场景中,比如最基础的代码示例:

ts 复制代码
new GLTFLoader().loade(...)

你可以从我的代码仓库里面获取模型,也可以自己去找一个喜欢能用的模型资源。

灯光创建

因为材质需要灯光的原因,所以我们还得加入灯光,这里我使用聚光灯效果来实现,添加灯光也非常的简单:

ts 复制代码
const ambientLight = new AmbientLight(0xffffff, 0.1);
const spotLight1 = new SpotLight(0xffffff, 200, 20, Math.PI * 0.3);
const spotLight2 = new SpotLight(0xffffff, 200, 20, Math.PI * 0.3);
spotLight1.position.set(3, 5, 0);
spotLight2.position.set(-3, 5, 0);
scene.add(ambientLight, spotLight1, spotLight2);

完成这几个简单的步骤后,我们就可以在场景中看见这辆酷炫的赛车了:

颜色更换

现在来实现动态修改赛车颜色的功能。

更换材质

因为我们需要自己来控制汽车车身等位置的颜色,所以需要我们自定义材质来覆盖之前的材质,代码示例:

ts 复制代码
    const bodyMaterial = new MeshPhysicalMaterial({
      color: this.debugParams.bodyColor, metalness: 0.6, roughness: 0.5, clearcoat: 0.725, clearcoatRoughness: 0.03
    });

    const tireMaterial = new MeshStandardMaterial({
      color: this.debugParams.tireColor, metalness: 1.0, roughness: 0.5
    });

    const glassMaterial = new MeshPhysicalMaterial({
      color: this.debugParams.glassColor, metalness: 0.7, roughness: 0, transmission: 0.8, clearcoatRoughness: 0.03
    });
    const lightMaterial = glassMaterial.clone();

    const obj = this.model.scene.children[0];
    // 车身
    const bodyObj = obj.getObjectByName('Object_10') as Mesh;
    bodyObj.material = bodyMaterial;
    // 玻璃
    const glassObj = obj.getObjectByName('Object_11') as Mesh;
    glassObj.material = glassMaterial;
    // 轮胎
    const tire1 = obj.getObjectByName('Circle008_5') as Object3D;
    const tire2 = obj.getObjectByName('Circle009_6') as Object3D;
    const tire3 = obj.getObjectByName('Circle010_7') as Object3D;
    const tire4 = obj.getObjectByName('Circle011_8') as Object3D;
    [tire1, tire2, tire3, tire4].forEach(tireObj => {
      tireObj.children.forEach(item => {
        (item as Mesh).material = tireMaterial;
      });
    });

因为每个模型对应的位置名称可能不同,所以通过getObjectByName方法获取对应对象时,应该先明确需要覆盖材质部位的名称,可以在Blender或其他建模软件中对照。

接下来,我再介绍材质对应的属性:

  • color:表示材质的颜色,也是我们需要更改的属性;
  • mataless:金属度,模拟金属光泽的;
  • roughness:粗糙度,越粗糙反光越差;
  • transmission:透明度,表示光线能穿透的程度;
  • clearcoat:清漆,可以表示额外的光泽效果;
  • clearcoatRoughness:清漆粗糙度,理解为控制清漆的效果强度;

调试器

当我们修改材质的时候,会不断手动修改代码来看效果,这种效率是很低的,所以需要通过一个调试器来解决这个问题,同时调试器也让我们有了动态修改汽车颜色的功能。

通过npm安装dat.gui插件:

shell 复制代码
npm i dat.gui

安装完成以后,通过下面所示代码来添加调试功能:

ts 复制代码
this.debugParams = {
  bodyColor: 0x49519e,
  tireColor: 0x8c8c8c,
  glassColor: 0xffffff,
  lightColor: 0x567bff,
}
this.addColorDebug('bodyColor', bodyMaterial, '车身颜色');
this.addColorDebug('tireColor', tireMaterial, '轮胎颜色');
this.addColorDebug('glassColor', glassMaterial, '玻璃颜色');
this.addColorDebug('lightColor', lightMaterial, '车前盖大灯颜色');

addColorDebug(key: string, material: MeshPhysicalMaterial | MeshStandardMaterial, name: string) {
  this.gui.addColor(this.debugParams, key).onFinishChange(() => {
    const color = new Color(this.debugParams[key]);
    material.color = color;
  }).name(name);
}

当然各人写法不同,但是思路都是一致的,通过dat.gui来动态修改颜色,然后将新的颜色赋值给对应的材质。 完成上面步骤后,我们来看看效果图:

可以看到,切换颜色的功能已经实现了。但是,切换颜色时效果比较突兀,这里我们通过使用gsap库来实现过渡的效果,实现很简单,只需要将上面的这行代码改为如下:

ts 复制代码
// material.color = color;
gsap.to(material.color, { r: color.r, g: color.g, b: color.b, duration: 1 });

现在再来看看修改之后的效果图:

其他效果

当然,我们还可以实现一些其他效果,例如在车的某个部位放一个标签,通过点击标签将摄像头移动过去。

标签

在汽车模型上放置标签我不知道怎么去实现比较好,就暂时通过一个CircleGeometry来放在一个合适的位置,如下图:

鼠标事件

通过Raycaster对象,我们可以实现当鼠标点击标签时的交互,代码如下:

ts 复制代码
    window.addEventListener('mousedown', evt => {
      const x = evt.clientX;
      const y = evt.clientY;
      mouse.x = (x / this.game.width) * 2 - 1;
      mouse.y = -(y / this.game.height) * 2 + 1;
      raycaster.setFromCamera(mouse, this.game.gameCamera.camera);
      // 检测是否点击到了标签
      const objects = raycaster.intersectObject(this.point.mesh);
      if (objects.length) {
        gsap.to(this.game.gameControls.controls.target, {
          x: x,
          y: y,
          z: z,
          duration: 1
        });
        gsap.to(this.game.gameControls.controls.object.position, {
          x: x,
          y: y,
          z: z,
          duration: 1,
          onComplete: () => {
              // do sth
          }
        });
      }
    });

通过RaycastersetFromCamera方法就可以实现鼠标点击物体的射线检测了,并且当触发事件后我们就可以做一些交互操作了,来看看实现后的效果图:

结语

这个例子的实现比较简单,没有什么比较复杂的点,所以很容易就能实现。

当然,也可以在车辆上做一些其他交互操作丰富场景,这里就不再继续说明了。

对应资料

three.js官方网站
模型资源网站
代码仓库地址

相关推荐
王哲晓9 分钟前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_41112 分钟前
无网络安装ionic和运行
前端·npm
理想不理想v14 分钟前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云24 分钟前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:1379712058726 分钟前
web端手机录音
前端
齐 飞31 分钟前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
神仙别闹1 小时前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
GIS程序媛—椰子2 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_0012 小时前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端2 小时前
Content Security Policy (CSP)
前端·javascript·面试