在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官方网站
模型资源网站
代码仓库地址

相关推荐
酷爱码10 分钟前
css中的 vertical-align与line-height作用详解
前端·css
沐土Arvin24 分钟前
深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器
开发语言·前端·javascript·设计模式·html
专注VB编程开发20年25 分钟前
VB.NET关于接口实现与简化设计的分析,封装其他类
java·前端·数据库
小妖66635 分钟前
css 中 content: “\e6d0“ 怎么变成图标的?
前端·css
L耀早睡1 小时前
mapreduce打包运行
大数据·前端·spark·mapreduce
HouGISer2 小时前
副业小程序YUERGS,从开发到变现
前端·小程序
outstanding木槿2 小时前
react中安装依赖时的问题 【集合】
前端·javascript·react.js·node.js
霸王蟹2 小时前
React中useState中更新是同步的还是异步的?
前端·javascript·笔记·学习·react.js·前端框架
霸王蟹2 小时前
React Hooks 必须在组件最顶层调用的原因解析
前端·javascript·笔记·学习·react.js
专注VB编程开发20年2 小时前
asp.net IHttpHandler 对分块传输编码的支持,IIs web服务器后端技术
服务器·前端·asp.net