Vue + Threejs Web 可视化搭建

项目背景

因旧项目升级改造需要,加以 WebGL 和元宇宙概念兴起,经过技术讨论最用采用 Vue + Threejs 技术栈对项目进行重新编写,此篇笔记只记录 Three.js 相关代码示例。项目配置相关和请求封装等不做介绍。

项目创建

TIP

  • Node.js v14.18.1 、yarn v1.22.17、@vue/cli 4.5.15、three.js 141.0 Vue2

使用 vueCLI 创建项目

vue create three-project

初始化画布

  1. 安装 three.js 依赖包 yarn add three
  2. 初始化画布,实现基础场景创建基础场景创建

项目实现效果

基础场景创建

<template>
  <div class="demo-box" id="demo"></div>
</template>

<script>
import {
  Scene,
  Color,
  Fog,
  PerspectiveCamera,
  AxesHelper,
  BoxGeometry,
  MeshStandardMaterial,
  Mesh,
  AmbientLight,
  PointLight,
  WebGLRenderer,
  sRGBEncoding
} from 'three'
// 引入鼠标控制(旋转、缩放)
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
let scene, renderer, camera
let ambient, point
let controls
export default {
  name: 'base_scene',
  components: {},
  mounted() {
    this.init()
    this.animate()
  },
  methods: {
    // 初始化
    init() {
      let _this = this

      /* 创建场景 */
      scene = new Scene()
      scene.background = new Color(0x443333)
      // 雾化,简单的说:场景中越远的位置看起
      scene.fog = new Fog(0x443333, 1, 1800)

      /* 创建相机 */
      camera = new PerspectiveCamera(
        120,
        window.innerWidth / window.innerHeight,
        1,
        1000
      )
      camera.position.z = 400
      camera.position.x = 200
      camera.position.y = 100

      /* 创建灯光 */
      _this.createLightingHandle()

      // 添加坐标系参考
      scene.add(new AxesHelper(1000))

      // 添加立方体
      let geometry, material, mesh
      geometry = new BoxGeometry(100, 100, 100)
      material = new MeshStandardMaterial({
        color: 'green',
        opacity: 0.25
      })
      mesh = new Mesh(geometry, material)
      mesh.position.set(0, 50, 0)
      scene.add(mesh)

      // 创建场景渲染器
      _this.rendererHandle()

      // 添加鼠标控制器
      _this.controlsHandle()

      //窗口大小变化监听
      window.addEventListener('resize', this.onWindowResize, false)
    },
    // 创建灯光
    createLightingHandle() {
      ambient = new AmbientLight(0xffffff, 0.5) //环境光
      ambient.position.set(200, 300, 200) //点光源位置
      scene.add(ambient)

      point = new PointLight(0xffffff, 0.5) //光源设置
      point.position.set(300, 320, 200) //点光源位置
      scene.add(point) //将光源添加到场景中
    },

    // 渲染场景
    rendererHandle() {
      // alpha: true
      renderer = new WebGLRenderer({ antialias: false })
      renderer.setPixelRatio(window.devicePixelRatio)
      renderer.setSize(window.innerWidth, window.innerHeight)
      renderer.shadowMap.enabled = true
      renderer.outputEncoding = sRGBEncoding
      document.getElementById('demo').appendChild(renderer.domElement)
    },
    // 鼠标控制器
    controlsHandle() {
      controls = new OrbitControls(camera, renderer.domElement)
      controls.target.set(0, 100, 0)
      controls.enableDamping = true
      // controls.maxDistance = 600 // 最大缩放
      // controls.minDistance = -35 // 最小缩放
      //上下翻转的最大角度,地面的法线方向
      // controls.maxPolarAngle = 1.5
      //上下翻转的最小角度,接近地面的角度
      // controls.minPolarAngle = 0.3
      controls.update()
    },
    // 添加动画
    animate() {
      requestAnimationFrame(this.animate)

      controls.update()

      renderer.render(scene, camera)
    },
    // 监听窗口大小
    onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight
      camera.updateProjectionMatrix()
      // 渲染窗口大小
      renderer.setSize(window.innerWidth, window.innerHeight)
    }
  },
  // 销毁方法
  destroyed() {
    this.init()
    this.animate()
    this.onWindowResize()

    window.removeEventListener('resize', this.onWindowResize, false)
  }
}
</script>
<style lang="scss" scoped>
.demo-box {
  overflow: hidden;
  height: 100%;
  width: 100%;
}
.adsorb-top {
  position: fixed;
  background: rgba(32, 83, 131, 0.6);
  width: 100%;
  padding: 20px;
  box-sizing: content-box;
  color: #ffffff;
  font-size: 1.125rem;
}
</style>

加载模型

加载 FBX 模型

<div id="container"></div>
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
// ....
// 初始化模型加载器
let fbxloader = new FBXLoader()
fbxloader.load('models/测试模型.fbx', (fbx) => {
  fbx.position.set(item.position.x, item.position.y, item.position.z) // 模型位置
  fbx.scale.set(0.02, 0.02, 0.02) // 模型缩放
  fbx.rotation.y = item.rotation.y // 模型旋转
  fbx.rotation.z = item.rotation.z // 模型旋转
  scene.add(fbx) // 添加fbx模型到场景中
})

//....

合并 mesh 对象

地面反射

后期合成地面漫反射

添加镜子反射物体

项目总结

模型对接问题

材质渲染器

三维软件内的材质和 threejs 的材质不一致问题

WebStock 实时更新 CreateLabel 创建标签

模型压缩-优化加载速度

模型场景清除