likes
comments
collection
share

3D 汽车颜色定制:Vue.js + Three.js 实战指南如何在Vue中使用Three.js?本文将带友友们在vu

作者站长头像
站长
· 阅读数 40

前言

Three.js 是一个流行的 JavaScript 库,简化了 WebGL 的使用,让我们能够在浏览器中轻松创建和展示 3D 图形。今天带友友们在Vue中使用Three.js,初步构建出功能丰富且易于维护的 3D Web 应用程序。本文将通过一个具体的例子——一个允许用户选择汽车颜色的交互式 3D, 展示如何在vue项目中使用 Three.js ,先上效果:

3D 汽车颜色定制:Vue.js + Three.js 实战指南如何在Vue中使用Three.js?本文将带友友们在vu

一:思路概述

我们的目标是创建一个基于 Web 的 3D 汽车展示应用,用户可以在这个应用中自由地改变汽车的颜色。为了实现这一目标,我们需要解决以下几个问题:

  1. 设置 Three.js 环境:初始化 Three.js 的基本组件,如场景、相机、渲染器等。
  2. 加载 3D 模型:使用 Three.js 的加载器加载外部 3D 模型。
  3. 交互设计:允许用户通过 UI 控制 3D 模型的状态。
  4. 性能优化:确保应用程序在不同的设备上都能流畅运行。

二:初始化 Three.js

2.1:场景和相机

首先,我们需要创建一个 Three.js 场景,并初始化一个透视相机。透视相机模仿人眼的视角,使远处的物体看起来更小。

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(3, 2, 2); // 设置相机位置
  • 这里的 40 表示视场角,决定了相机视角的宽度;
  • window.innerWidth / window.innerHeight 计算屏幕的宽高比,确保场景正确填充窗口;
  • 0.11000 分别是近裁剪平面和远裁剪平面的距离,超出这两个距离的对象不会被渲染。

2.2:渲染器

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth * 0.9, window.innerHeight * 0.8);
document.body.appendChild(renderer.domElement);
  • WebGLRenderer 是 Three.js 中用于渲染场景的主要类。这里设置了抗锯齿 (antialias) 和透明度支持 (alpha)。
  • setSize 方法用于设置渲染器输出的尺寸,使其适应屏幕大小。
  • 最后把渲染器的 DOM 元素添加到页面中。

2.3:动画循环

function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

requestAnimationFrame 方法用于请求浏览器在下一次重绘之前调用指定的回调函数。这样可以保证渲染器在每一帧都更新场景,从而创建出动画效果。

三:加载 3D 模型

3.1:导入模型

为了能够看到一辆真实的汽车,我们需要加载一个 3D 模型。在这个例子中使用的是 .glb 格式的模型,它是一种包含了模型的所有信息(几何体、纹理等)的紧凑格式。

const loader = new THREE.GLTFLoader();
const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath("../../public/roadSter/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
  • GLTFLoader 负责加载 .glb 文件
  • DRACOLoader 则用于解压模型数据中的 Draco 压缩格式。Draco 是一种高效的 3D 压缩算法,能够显著减小 3D 模型的文件大小。

3.2:模型处理

加载模型后,我们还需要对模型进行一些处理,例如更改材质或调整位置等。

loader.load("../../public/roadSter/model/bmw01.glb", gltf => {
  const bmw = gltf.scene;
  bmw.traverse(child => {
    if (child.isMesh) {
      // 根据模型的命名规则来更改材质
      switch (child.name) {
        case "轮毂":
          child.material = wheelsMaterial;
          break;
        case "车身":
          child.material = bodyMaterial;
          break;
        // 更多的材质更改...
      }
    }
  });
  scene.add(bmw);
});

这里遍历了模型的每个网格对象,并根据它们的名字来更改相应的材质。traverse 方法用于遍历场景图中的所有对象,这对于查找特定类型的节点非常有用。

四:交互设计

为了能与 3D 模型互动,需要添加一些 UI 控件,这里以添加一个颜色选择器为例,让用户可以选择不同的汽车颜色。

const selectColor = color => {
  bodyMaterial.color.set(color);
  // 更改其他相关材质的颜色...
};

let colors = [
  {
    name: "冷光银",
    color: "#424449"
  },
  // 更多颜色选项...
];

定义了一个 selectColor 函数,当用户选择一个颜色时,这个函数会被调用来更新汽车的材质颜色。同时,我们创建了一个 colors 数组来存储可用的颜色选项。

在 Vue.js 的模板中,使用 v-for 指令来动态生成颜色选择按钮。

<div class="select">
  <div 
    class="select-item"
    v-for="(item, index) in colors"
    :key="index"
    @click="selectColor(item.color)"
  >
    <div class="select-item-color" :style="{ background: item.color }"></div>
    <div class="select-item-name">{{ item.name }}</div>
  </div>
</div>

这里使用了 Vue.js 的数据绑定特性,当用户点击某个颜色时,对应的事件处理器将会被触发,进而改变汽车的颜色。

五:性能优化

开发过程中,性能问题也是十分主要的一个点,特别是在我们的这个3D交换渲染过程,这里采用缓存来减少加载时间,或者使用适当的光照来提高渲染效率。

// 使用缓存来减少重复加载相同的模型
loader.loadModel = function(url) {
  return new Promise((resolve, reject) => {
    loader.load(url, resolve, undefined, reject);
  });
};

// 缓存模型加载
const loadModel = async url => {
  return await loader.loadModel(url);
};

此外,还可以考虑使用 THREE.LoadingManager 来管理加载过程,或者使用 THREE.LoadingScreen 来显示加载进度条。这些可以提高前端用户的体验,特别是在网络条件不佳的情况下。

六:结构与组件化

将 3D 汽车展示的部分封装成一个独立的 Vue 组件,这样可以方便地复用代码并保持代码的清晰性。

  • 利用 Vue.js 的组合 API 来组织代码
  • onMounted 钩子确保在组件挂载后执行 Three.js 的初始化代码
// 3DCar.vue
<template>
  <div ref="canvasContainer" />
</template>

<script>
import { onMounted, ref } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';

export default {
  setup() {
    const canvasContainer = ref(null);

    onMounted(() => {
      const scene = new THREE.Scene();
      const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.set(3, 2, 2);

      const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      renderer.setSize(window.innerWidth * 0.9, window.innerHeight * 0.8);
      canvasContainer.value.appendChild(renderer.domElement);

      const loader = new GLTFLoader();
      const dracoLoader = new DRACOLoader();
      dracoLoader.setDecoderPath('../../public/roadSter/draco/gltf/');
      loader.setDRACOLoader(dracoLoader);

      loader.load('../../public/roadSter/model/bmw01.glb', gltf => {
        const bmw = gltf.scene;
        scene.add(bmw);
      });

      const animate = () => {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      };
      animate();
    });

    return {
      canvasContainer,
    };
  },
};
</script>
转载自:https://juejin.cn/post/7402926506543054859
评论
请登录