vue + threejs 实现3d装车

安装

npm install threejs -D

引用threejs

import * as THREE from 'three'
// 鼠标控制器
import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// obj模型
import {
    
     OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
// 材质
import {
    
     MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'

场景
在threejs中场景就只有一种,用THREE.Scene来表示,要构建一个场景只要new一个对象就可以了。

 this.scene = new THREE.Scene()

属性

属性 描述
children 数组,用于存储添加到场景中的所有对象
fog 雾化,雾化效果的特点是场景中的物体离得越远就会变得越模糊,有三个参数:雾的颜色,最近距离,最远距离
方法
方法 描述
Add() 向场景中添加对象
Remove() 移除场景中的对象
getObjectByName() 获取场景中指定名称的对象
tranverse() 以一个方法作为参数,这个方法将会在每一个子对象上执行。如果子对象本身还有子对象,该方法将会在所有的子对象上执行,直到遍历完场景树中的所有对象为止
相机
相机决定了场景中那个角度的景色会显示出来,相机就像人的眼睛一样,站在不同的位置,抬头或者低头都能看到不同的景色。分为两种正投影相机THREE.OrthographicCamera和透视投影相机THREE.PerspectiveCamera。
正投影与透视透视投影区别
正投影相机
OrthographicCamera( left, right, top, bottom, near, far )
  1. left: 渲染空间的左边界
  2. right: 渲染空间的右边界
  3. top:渲染空间的上边界
  4. bottom:渲染空间的下边界
  5. near: near属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1
  6. far:far属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值1000

透视投影相机
PerspectiveCamera( fov, aspect, near, far )

this.camera = new THREE.PerspectiveCamera(45, this.rendererWidth / this.rendererHeight, 0.1, 10000000)
  1. fov: 视角fov,视角的大小,如果设置为0,相当你闭上眼睛了,所以什么也看不到,如果为180,那么可以认为你的视界很广阔,但是在180度的时候,往往物体很小,因为他在你的整个可视区域中的比例变小了。一般情况下45
  2. near: 表示你近处的裁面的距离,也可以认为是眼睛距离近处的距离(>0)
  3. aspect: 实际窗口的纵横比,即宽度除以高度。这个值越大,说明你宽度越大,那么你可能看的是宽银幕电影了,如果这个值小于1

渲染器
Three.js中的场景是一个物体的容器,开发者可以将需要的角色放入场景中;
相机的作用就是面对场景,在场景中取一个合适的景,把它拍下来;
渲染器的作用就是将相机拍摄下来的图片,放到浏览器中去显示。
new THREE.WebGLRenderer()

属性 含义
antialias 是否开启反锯齿,设置为true开启反锯齿。
alpha 是否可以设置背景色透明。
maxLights 最大灯光数,我们的场景中最多能够添加多少个灯光。
logarithmicDepthBuffer 模型的重叠部位不停的闪烁。这便是Z-Fighting问题,为解决这个问题,我们可以采用该种方法
方法 含义
setSize 制定渲染器的宽高,renderer.setSize(width,height)
setClearColor 设置canvas背景色(clearColor)和背景色透明度(clearAlpha)
setPixelRatio 设置分辨率,解决场景模糊,抗锯齿的一种很好的方法
 this.renderer = new THREE.WebGLRenderer({
    
    
  antialais: true,
    alpha: true,
    logarithmicDepthBuffer: true,
  })
  this.renderer.setSize(this.rendererWidth, this.rendererHeight)
  this.renderer.setClearColor(0x39609b)
  this.renderer.setPixelRatio(window.devicePixelRatio)
  const container = document.getElementById('canvasContainer')
  container.appendChild(this.renderer.domElement)

threejs中的坐标系
在这里插入图片描述
threejs中的灯光

光源种类 含义
环境光(AmbientLight) 笼罩在整个空间无处不在的光,不能产生阴影
点光源(PointLight ) 向四面八方发射的单点光源,不能产生阴影
聚光灯(SpotLight ) 锥形效果的光源,能够产生阴影
平行光(DirectinalLight) 平行光,类似太阳光,距离很远的光,会产生阴影
// 环境光
this.ambient = new THREE.AmbientLight(0xffffff, 1)
this.ambient.position.set(0, 0, 0)
this.scene.add(this.ambient)
// 平行光
this.directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)
this.directionalLight.position.set(0, 200, 0)
this.scene.add(this.directionalLight)
// 设置点光源
this.pointLight1 = new THREE.PointLight(0xffffff, 0.3)
this.pointLight1.position.set(-500, 200, 0)
this.scene.add(this.pointLight1)
this.pointLight2 = new THREE.PointLight(0xffffff, 0.3)
this.pointLight2.position.set(500, 200, 0)
this.scene.add(this.pointLight2)

模型加载
实际开发中,大多数项目,通常是3D美术设计师或建筑、机械等行业工程师提供的由3dmx、blender、substence、Solidworks等软件创建好的三维模型文件
1.加载.obj模型文件:
使用三维软件导出 .obj 模型文件的时候,会同时导出一个材质文件 .mtl , .obj 和 .stl 文件包含的信息一样都是几何体顶点相关数据,材质文件 .mtl 包含的是模型的材质信息,比如颜色、贴图路径等。
加载 .obj 三维模型的时候,可以只加载 .obj 文件,然后借助three.js引擎自定义材质Material,也可以同时加载 .obj 和 .mtl 文件。
只加载obj文件:

<!-- 引入obj模型加载库OBJLoader.js -->
<script src="../../three.js-master/examples/js/loaders/OBJLoader.js"></script>
/**
* OBJ文件加载 只加载obj文件中的几何信息,不加载材质文件.mtl
*/
var loader = new THREE.OBJLoader();
// 没有材质文件,系统自动设置Phong网格材质
loader.load('./立方体/box.obj',function (obj) {
    
    
// 控制台查看返回结构:包含一个网格模型Mesh的组Group
console.log(obj);
// 查看加载器生成的材质对象:MeshPhongMaterial
console.log(obj.children[0].material);
scene.add(obj);
})

// 加载后的一些编辑操作
obj.children[0].scale.set(20,20,20);//网格模型缩放
obj.children[0].geometry.center();//网格模型的几何体居中
obj.children[0].material.color.set(0xff0000);//设置材质颜色

同时加载obj文件和mtl文件:
mtl 文件包含了模型的材质信息,比如模型颜色、透明度等信息,还有纹理贴图的路径,比如颜色贴图、法线贴图、高光贴图等等。

<!-- 引入obj模型加载库OBJLoader.js -->
<script src="../../three.js-master/examples/js/loaders/OBJLoader.js"></script>
<!-- 引入obj模型材质加载库MTLLoader.js -->
<script src="../../three.js-master/examples/js/loaders/MTLLoader.js"></script>
/**
* OBJ和材质文件mtl加载
*/
var OBJLoader = new THREE.OBJLoader();//obj加载器
var MTLLoader = new THREE.MTLLoader();//材质文件加载器
MTLLoader.load('./立方体/box.mtl', function(materials) {
    
    
// 返回一个包含材质的对象MaterialCreator
console.log(materials);
//obj的模型会和MaterialCreator包含的材质对应起来
OBJLoader.setMaterials(materials);
OBJLoader.load('./立方体/box.obj', function(obj) {
    
    
console.log(obj);
obj.scale.set(10, 10, 10); //放大obj组对象
scene.add(obj);//返回的组对象插入场景中
})
})

完整代码:

// html
<div id="canvasContainer" />
// js
// json 数据
const pageData = {
    
    container: {
    
    length: 15670,width: 2870,height: 3300,},
  boxlist: [{
    
    color: 'rgba(235,215,0,0.5)',x: 0,length: 2120, width: 1200,y: 0,z: 0,boxId: '5GD821021(左前叶子板)_1',height: 1640,},
    {
    
     color: 'rgba(235,0,215,0.5)',x: 0,length: 2120,width: 1200,y: 1200,z: 0,boxId: '5GD821022(右前叶子板)_1',height: 1640,},
    {
    
    color: 'rgba(215,215,30,0.5)',: 2120,length: 1785,width: 2160,y: 0,z: 0,boxId: '5GD823031(发动机盖总成)_1',height: 1530,},
    {
    
    color: 'rgba(215,215,30,0.5)',x: 2120,length: 1785,width: 2160,y: 0,z: 1530,boxId: '5GD823031(发动机盖总成)_1', height: 1530,},
    {
    
    color: 'rgba(215,30,215,0.5)',x: 3905,length: 1700,width: 2150,y: 0,z: 0,boxId: '3CG827025E(后+R58+F57:Q57)_1',height: 1450,},
    {
    
    color: 'rgba(215,195,60,0.5)',x: 3905,length: 1700, width: 2150,y: 0,z: 1450,boxId: '3CG827025E(后+R58+F57:Q57)_1',height: 1450,},
    {
    
    color: 'rgba(195,215,60,0.5)',x: 5605,length: 2160,width: 1350,y: 0,z: 0,boxId: '3CG831055D(左前门焊接总成)_1',height: 1700,},
    {
    
    color: 'rgba(195,60,215,0.5)',x: 5605,length: 2150,width: 1330,y: 0,z: 1700,boxId: '5DD831055(左前门)_1',height: 1465,},
    {
    
    color: 'rgba(215,175,90,0.5)',x: 7765,length: 2160,width: 1350, y: 0,z: 0,boxId: '3CG831056D(右前门焊接总成)_1',height: 1700,},
    {
    
    color: 'rgba(175,215,90,0.5)',x: 7765,length: 2150,width: 1330,y: 0,z: 1700,boxId: '5DD833056(右后门)_1',height: 1465,},
    {
    
    color: 'rgba(175,90,215,0.5)',x: 9925,length: 2160,width: 1350,y: 0,z: 0,boxId: '3CG831056D(右前门焊接总成)_2',height: 1700,},
    {
    
    color: 'rgba(215,155,120,0.5)',x: 9925,length: 2150,width: 1330,y: 0,z: 1700,boxId: '5GD831055A(左前门)_1', height: 1465,},
    {
    
    color: 'rgba(155,215,120,0.5)',x: 12085,length: 2160,width: 1280,y: 0,z: 0,boxId: '2GG831051(车门)_1',height: 1560,},
    {
    
     color: 'rgba(155,120,215,0.5)',x: 12085,length: 2160, width: 1280,y: 0,z: 1560,boxId: '2GG831051(车门)_1',height: 1560,},
    {
    
    color: 'rgba(215,135,150,0.5)',x: 14245,length: 1280,width: 1800,y: 0,z: 0,boxId: '2GG821101(翼子板)_1',height: 1500,},
    {
    
    color: 'rgba(135,215,150,0.5)',x: 14245,length: 1280,width: 1800,y: 0,z: 1500,boxId: '2GG821102(翼子板)_1',height: 1500,},
    {
    
    color: 'rgba(135,150,215,0.5)',x: 5605,length: 2120,width: 1200, y: 1350,z: 0,boxId: '5GD821021(左前叶子板)_1',height: 1640,},
    {
    
    'color': 'rgba(215,115,180,0.5)',x: 7725,length: 2120,width: 1200,y: 1350, z: 0,boxId: '5GD821021(左前叶子板)_2',height: 1640,},
    {
    
    color: 'rgba(115,215,180,0.5)',x: 9845,length: 2120,width: 1200,y: 1350,z: 0,boxId: '5GD821021(左前叶子板)_3',height: 1640,},
    {
    
    color: 'rgba(115,180,215,0.5)',x: 12085,length: 2160,width: 1280,y: 1280,z: 0, boxId: '5GD833055A(左后门)_1',height: 1480,},
    {
    
    color: 'rgba(215,95,210,0.5)', x: 12085,length: 2160,width: 1280,y: 1280,z: 1480,boxId: '5GD833056A(右后门)_1',height: 1480,},,}
export default {
    
    
  data(){
    
    
    return {
    
    
       renderer: null, // 渲染器
		scene: null, // 场景
		camera: null, // 相机
		ambient: null, // 环境光
		directionalLight: null, // 平行光
		pointLight1: null, // 点光源1
		pointLight2: null, // 点光源2
		controls: null, // 轨道控件
		carLength: 0,
	    carHeight: 0,
	    carWidth: 0,
    }
  },
  methods: {
    
    
        threeInit() {
    
    
      // 初始化渲染器
      this.initThree()
      // 初始化场景
      this.initScene()
      // 初始化相机
      this.initCamera()
      // 初始化灯光
      this.initLight()
      // 加载obj文件
      this.initAgv()
      // 初始化控件
      this.initControls()
      // 循环渲染
      this.animation()
    },
    initThree() {
    
    
      this.renderer = new THREE.WebGLRenderer({
    
    
        antialais: true,
        alpha: true,
        logarithmicDepthBuffer: true,
      })
      this.renderer.setSize(this.rendererWidth, this.rendererHeight)
      this.renderer.setClearColor(0x39609b)
      this.renderer.setPixelRatio(window.devicePixelRatio)
      const container = document.getElementById('canvasContainer')
      container.appendChild(this.renderer.domElement)
    },
    initScene() {
    
    
      this.scene = new THREE.Scene()
    },
    initCamera() {
    
    
      this.camera = new THREE.PerspectiveCamera(45, this.rendererWidth / this.rendererHeight, 0.1, 10000000)
      // 相机位置
      var conta = {
    
     length: pageData.container.length, width: pageData.container.width, height: pageData.container.height }
      this.camera.position.set(conta.length * 2, conta.width * 2, conta.height * 2)
      // 相机朝向
      this.camera.lookAt(this.scene.position)
      this.scene.add(this.camera)
    },
    initLight() {
    
    
      // 环境光
      this.ambient = new THREE.AmbientLight(0xffffff, 1)
      this.ambient.position.set(0, 0, 0)
      this.scene.add(this.ambient)
      // 平行光
      this.directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)
      this.directionalLight.position.set(0, 200, 0)
      this.scene.add(this.directionalLight)
      // 设置点光源
      this.pointLight1 = new THREE.PointLight(0xffffff, 0.3)
      this.pointLight1.position.set(-500, 200, 0)
      this.scene.add(this.pointLight1)
      this.pointLight2 = new THREE.PointLight(0xffffff, 0.3)
      this.pointLight2.position.set(500, 200, 0)
      this.scene.add(this.pointLight2)
    },
    initAgv() {
    
    
      const objLoader = new OBJLoader()
      const mtlLoader = new MTLLoader()
      mtlLoader.load('static/model/car.mtl', (materials) => {
    
    
        objLoader.setMaterials(materials)
        objLoader.load('static/model/car.obj', (obj) => {
    
    
          obj.scale.set(20, 20, 20) // 网格模型缩放
          const bbox = new THREE.Box3().setFromObject(obj)

          var carHead = 5617 // 车头长度 = (bbox.max.z - bbox.min.z)/2 - 7866
          this.carLength = bbox.max.z - bbox.min.z - carHead // 车箱长度,不含车头
          this.carHeight = bbox.max.x - bbox.min.x
          this.carWidth = bbox.max.y - bbox.min.y
          var difx = (bbox.max.x - bbox.min.x) / 2
          obj.rotation.y = -Math.PI / 2
          obj.translateZ(-7866)
          obj.translateY(-2500)
          obj.translateX(2500)
          this.scene.add(obj)
        })
      })
    },
    initControls() {
    
    
      // x 红 y 绿 z 蓝
      this.controls = new OrbitControls(this.camera, this.renderer.domElement) // 创建控件对象
      var axes = new THREE.AxisHelper(6000)
      this.scene.add(axes)
    },
    animation() {
    
    
      requestAnimationFrame(this.animation)
      this.controls.update()
      this.renderer.render(this.scene, this.camera)
    },
    onLoading() {
    
    
      this.drawPackage(pageData)
    },
    drawPackage(data) {
    
    
      const carData = data.container
      const packData = data.boxlist
      packData.forEach((item) => {
    
    
        var realVirtualRatio = Math.min(this.carWidth / carData.width, this.carHeight / carData.height, this.carLength / carData.length)
        var goodsLength = item.length * realVirtualRatio
        var goodsWidth = item.width * realVirtualRatio
        var goodsHeight = item.height * realVirtualRatio
        var goodsX = item.x * realVirtualRatio
        var goodsY = item.y * realVirtualRatio
        var goodsZ = item.z * realVirtualRatio
        const box = new THREE.BoxGeometry(goodsLength, goodsWidth, goodsHeight)
        const Mesh = new THREE.MeshLambertMaterial({
    
    
          color: item.color,
        })
        // 画出网格图形并且定义材质(颜色)
        var MeshItem = new THREE.Mesh(box, Mesh)
        MeshItem.uuid = item.boxId
        MeshItem.type = item.type
        MeshItem.position.copy(new THREE.Vector3(goodsX + goodsLength / 2, goodsY + goodsWidth / 2, goodsZ + goodsHeight / 2))
        MeshItem.castShadow = !0
        MeshItem.receiveShadow = !0
        this.scene.add(MeshItem)
      })
    },
  }
}

装车后的效果

猜你喜欢

转载自blog.csdn.net/qq_40121308/article/details/119023975